A defensive check validates a value at an API boundary before the value reaches fields or later logic. In Java, this commonly means rejecting null inputs and out-of-range indexes at public methods or constructors, so a bad call fails near its cause rather than producing a later NullPointerException or corrupted state. section 5.5 section 5.1.1
Use a check when null is not part of the contract. Objects.requireNonNull() makes that contract explicit and prevents an object from being created with a required field missing. Collections deserve the same consideration: accepting a collection while silently retaining null elements moves the failure to an unrelated iteration or lookup later. section 5.1.1 section 5.1.1
public class Person {
private final String firstName, lastName;
public Person(String firstName, String lastName) {
this.firstName = Objects.requireNonNull(firstName);
this.lastName = Objects.requireNonNull(lastName);
}
}
A constructor checks required names before assigning them to final fields, making invalid Person instances impossible through this constructor. section 5.1.1
public void process(Collection<String> data) {
data.forEach(Objects::requireNonNull);
…
}
Checking every collection element establishes a non-null element contract before subsequent processing begins. section 5.1.1
Keep defensive checks focused on values the boundary promises to accept:
- Check required arguments immediately, especially before saving them to fields. section 5.5
- Check indexes against the expected range before using or storing them. section 5.5
- Validate that input collections contain no nulls when later processing requires non-null elements; account for the possible performance cost. section 5.1.1
- Prefer an exception with detailed error information when an error cannot be recovered from, rather than returning null. section 5.1.1
A defensive check is not a reason to use null as an error signal elsewhere. Returning null for Collection, Stream, Optional, arrays, maps, or collection elements leaves callers to discover an undocumented exceptional state. Return an empty container when that represents the result; where a third state is meaningful, make it explicit with an enum rather than a boxed Boolean that can be null. section 5.1.1 section 5.1.1
Make a three-way answer explicit
A confirmation action with Yes, No, and Cancel cannot be represented safely by a boolean without losing or hiding one outcome. An enum such as YES, NO, CANCEL lets a switch handle each result directly, avoiding null unboxing and making cancellation visible in the control flow. section 5.1.1
Static analysis can reinforce a non-null API contract but does not replace boundary checks. IntelliJ IDEA, SonarLint, and SpotBugs can report explicit null returns where Optional, collections, or arrays are expected; warnings still need review against the intended contract. section 5.1.1
