Background
Java's assert keyword has specific intended use cases and runtime behavior that may not align with our current usage patterns in this repository.
Java assert Conventions and Behavior
According to Java best practices and Oracle documentation:
- Intended use: Internal invariant checking, debugging, and development-time verification
- Runtime behavior: Assertions are disabled by default and require explicit JVM flags (
-ea/-enableassertions) to activate
- Purpose: Help developers catch bugs during development and testing, not for production error handling
- Expected outcome: When disabled (default), assert statements are completely ignored and have zero performance impact
Current Issues in Our Codebase
I've identified several instances where we're using assert for input validation and precondition checking in public APIs. This approach has several significant drawbacks:
- Inconsistent behavior: Same input produces different results depending on JVM assertion settings
- Silent failures: Invalid inputs may be silently accepted when assertions are disabled (default behavior)
- Unclear error handling: Users don't know what exceptions to expect from our APIs
- Testing gaps: Our tests might pass in development (with
-ea) but fail silently in production
Examples in Our Codebase
|
assert singleton == null; |
|
E e = CollectionUtils.getOne(c); |
|
singleton = Objects.requireNonNull(e, NULL_MESSAGE); |
|
public IsNullConditionDecision( |
|
If stmt, Var varTested, |
|
IsNullValue ifTrueDecision, IsNullValue ifFalseDecision) { |
|
assert varTested.getType() instanceof ReferenceType; |
|
assert !(ifTrueDecision == null && ifFalseDecision == null); |
|
this.conditionStmt = stmt; |
|
@Override |
|
protected void validate() { |
|
assert (Exps.holdsInt(operand1) && Exps.holdsInt((operand2)) || |
|
(Exps.holdsLong(operand1) && Exps.holdsLong(operand2))); |
|
} |
|
Obj getMetaObjArray(Invoke invoke) { |
|
assert Set.of("getConstructors", "getDeclaredConstructors", |
|
"getMethods", "getDeclaredMethods", |
|
"getFields", "getDeclaredFields").contains(invoke.getMethodRef().getName()); |
|
return heapModel.getMockObj(REFLECTION_ARRAY_DESC, invoke, |
|
invoke.getMethodRef().getReturnType(), invoke.getContainer()); |
|
} |
... more than 50 assert statements.
Proposed Solution
I suggest we replace assertion-based validation with explicit exception throwing:
Before (Current Approach)
public void processData(String input) {
assert input != null : "Input cannot be null";
assert !input.isEmpty() : "Input cannot be empty";
// processing logic
}
After (Recommended Approach)
public void processData(String input) {
if (input == null) {
throw new IllegalArgumentException("Input cannot be null");
}
if (input.isEmpty()) {
throw new IllegalArgumentException("Input cannot be empty");
}
// processing logic
}
Alternative: Utility Methods
public void processData(String input) {
Objects.requireNonNull(input, "Input cannot be null");
if (input.isEmpty()) {
throw new IllegalArgumentException("Input cannot be empty");
}
// processing logic
}
Real-World Best Practices
-
Spring Framework: Created its own org.springframework.util.Assert utility class that throws explicit exceptions (IllegalArgumentException, IllegalStateException) instead of using native assert keyword
-
Google Guava: Developed Preconditions class with methods like checkArgument() and checkState() that throw IllegalArgumentException and IllegalStateException respectively, completely avoiding the native assert keyword
-
Apache Commons: Provides Validate.isTrue() and similar methods that throw IllegalArgumentException for input validation, explicitly designed for cases where assertions would be inappropriate due to their ability to be disabled at runtime
Benefits
- ✅ Consistent behavior regardless of JVM settings
- ✅ Clear API contracts with documented exceptions
- ✅ Better error messages for library users
- ✅ Proper separation between debugging aids and production error handling
Discussion Points
- Should we establish coding guidelines or checkstyle rules about when to use
assert vs explicit validation?
- Do we want to introduce a utility class (like Guava's
Preconditions) for common validations?
- Are there any performance-critical paths where we might want to keep assertions for internal checks?
Background
Java's
assertkeyword has specific intended use cases and runtime behavior that may not align with our current usage patterns in this repository.Java
assertConventions and BehaviorAccording to Java best practices and Oracle documentation:
-ea/-enableassertions) to activateCurrent Issues in Our Codebase
I've identified several instances where we're using
assertfor input validation and precondition checking in public APIs. This approach has several significant drawbacks:-ea) but fail silently in productionExamples in Our Codebase
Tai-e/src/main/java/pascal/taie/util/collection/AbstractHybridSet.java
Lines 167 to 169 in 12e7c43
Tai-e/src/main/java/pascal/taie/analysis/bugfinder/nullpointer/IsNullConditionDecision.java
Lines 42 to 47 in 12e7c43
Tai-e/src/main/java/pascal/taie/ir/exp/BitwiseExp.java
Lines 58 to 62 in 12e7c43
Tai-e/src/main/java/pascal/taie/analysis/pta/plugin/reflection/MetaObjHelper.java
Lines 141 to 147 in 12e7c43
... more than 50
assertstatements.Proposed Solution
I suggest we replace assertion-based validation with explicit exception throwing:
Before (Current Approach)
After (Recommended Approach)
Alternative: Utility Methods
Real-World Best Practices
Spring Framework: Created its own
org.springframework.util.Assertutility class that throws explicit exceptions (IllegalArgumentException,IllegalStateException) instead of using nativeassertkeywordGoogle Guava: Developed
Preconditionsclass with methods likecheckArgument()andcheckState()that throwIllegalArgumentExceptionandIllegalStateExceptionrespectively, completely avoiding the nativeassertkeywordApache Commons: Provides
Validate.isTrue()and similar methods that throwIllegalArgumentExceptionfor input validation, explicitly designed for cases where assertions would be inappropriate due to their ability to be disabled at runtimeBenefits
Discussion Points
assertvs explicit validation?Preconditions) for common validations?