0
# Null Safety
1
2
Comprehensive null safety annotation system for expressing nullability constraints and enabling safer code through static analysis. These annotations integrate with SpotBugs and other tools to detect null pointer exceptions at compile time.
3
4
## Capabilities
5
6
### NonNull Annotation
7
8
Indicates that the annotated element must not be null.
9
10
```java { .api }
11
/**
12
* The annotated element must not be null.
13
*
14
* Annotated Fields must only not be null after construction has completed.
15
* Annotated methods must have non-null return values.
16
*/
17
@Documented
18
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE })
19
@Retention(RetentionPolicy.CLASS)
20
@javax.annotation.Nonnull(when = When.ALWAYS)
21
@TypeQualifierNickname
22
@interface NonNull {
23
}
24
```
25
26
**Usage Examples:**
27
28
```java
29
public class UserService {
30
// Non-null field - must be initialized
31
@NonNull
32
private final UserRepository repository;
33
34
public UserService(@NonNull UserRepository repository) {
35
this.repository = repository; // Required - cannot be null
36
}
37
38
// Non-null return value
39
@NonNull
40
public String getUserName(@NonNull String userId) {
41
// Must return non-null value
42
String name = repository.findNameById(userId);
43
return name != null ? name : "Unknown";
44
}
45
46
// Non-null parameter
47
public void updateUser(@NonNull User user) {
48
// Can safely use user without null check
49
repository.save(user);
50
}
51
}
52
```
53
54
### Nullable Annotation
55
56
Indicates that the annotated element could be null under some circumstances.
57
58
```java { .api }
59
/**
60
* The annotated element could be null under some circumstances.
61
*
62
* In general, this means developers will have to read the documentation to
63
* determine when a null value is acceptable and whether it is necessary to
64
* check for a null value.
65
*
66
* When this annotation is applied to a method it applies to the method return value.
67
*/
68
@Documented
69
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE })
70
@Retention(RetentionPolicy.CLASS)
71
@javax.annotation.Nonnull(when = When.UNKNOWN)
72
@TypeQualifierNickname
73
@interface Nullable {
74
}
75
```
76
77
**Usage Examples:**
78
79
```java
80
public class UserService {
81
// Nullable field - may be null
82
@Nullable
83
private String cachedData;
84
85
// Nullable return value - may return null
86
@Nullable
87
public User findUser(@NonNull String userId) {
88
return repository.findById(userId); // May return null if not found
89
}
90
91
// Nullable parameter - null is acceptable
92
public void setDisplayName(@Nullable String displayName) {
93
// Must check for null before use
94
this.displayName = displayName != null ? displayName : "Anonymous";
95
}
96
97
// Handle nullable values safely
98
public void processUser(@NonNull String userId) {
99
@Nullable User user = findUser(userId);
100
if (user != null) {
101
// Safe to use user here
102
user.updateLastAccess();
103
}
104
}
105
}
106
```
107
108
### CheckForNull Annotation
109
110
Indicates that the annotated element might be null and uses of the element should check for null.
111
112
```java { .api }
113
/**
114
* The annotated element might be null, and uses of the element should check for
115
* null.
116
*
117
* When this annotation is applied to a method it applies to the method return value.
118
*/
119
@Documented
120
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE })
121
@Retention(RetentionPolicy.CLASS)
122
@javax.annotation.Nonnull(when = When.MAYBE)
123
@TypeQualifierNickname
124
@interface CheckForNull {
125
}
126
```
127
128
**Usage Examples:**
129
130
```java
131
public class CacheService {
132
// Check for null before using
133
@CheckForNull
134
public String getCachedValue(@NonNull String key) {
135
String value = cache.get(key);
136
return value; // Explicitly check for null before using result
137
}
138
139
public void processData(@NonNull String key) {
140
@CheckForNull String data = getCachedValue(key);
141
142
// Must check for null - static analysis will warn if you don't
143
if (data != null) {
144
System.out.println(data.length());
145
} else {
146
System.out.println("No cached data found");
147
}
148
}
149
}
150
```
151
152
### UnknownNullness Annotation
153
154
Indicates that the nullness of the annotated element is unknown, or may vary in unknown ways in subclasses.
155
156
```java { .api }
157
/**
158
* Used to indicate that the nullness of element is unknown, or may vary in
159
* unknown ways in subclasses.
160
*/
161
@Documented
162
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE })
163
@Retention(RetentionPolicy.CLASS)
164
@javax.annotation.Nonnull(when = When.UNKNOWN)
165
@TypeQualifierNickname
166
@interface UnknownNullness {
167
}
168
```
169
170
**Usage Examples:**
171
172
```java
173
public class LegacyIntegration {
174
// Nullness unknown - treat with caution
175
@UnknownNullness
176
public Object callLegacyApi(@NonNull String request) {
177
// Legacy API with unclear null handling
178
return legacySystem.process(request);
179
}
180
181
public void handleLegacyResult(@NonNull String request) {
182
@UnknownNullness Object result = callLegacyApi(request);
183
184
// Defensive programming - assume it might be null
185
if (result != null) {
186
processResult(result);
187
}
188
}
189
}
190
```
191
192
### PossiblyNull Annotation (Deprecated)
193
194
Indicates that the annotated element might be null, and uses of the element should check for null.
195
196
```java { .api }
197
/**
198
* The annotated element should might be null, and uses of the element should
199
* check for null.
200
*
201
* When this annotation is applied to a method it applies to the method return
202
* value.
203
*
204
* @deprecated - use CheckForNull instead; the name of which more clearly
205
* indicates that not only could the value be null, but that good
206
* coding practice requires that the value be checked for null.
207
*/
208
@Documented
209
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE })
210
@Retention(RetentionPolicy.CLASS)
211
@javax.annotation.Nonnull(when = When.MAYBE)
212
@TypeQualifierNickname
213
@Deprecated
214
@interface PossiblyNull {
215
}
216
```
217
218
**Usage Examples (Deprecated - Use @CheckForNull instead):**
219
220
```java
221
public class LegacyService {
222
// Legacy usage - prefer @CheckForNull
223
@PossiblyNull
224
@Deprecated
225
private final Logger logger;
226
227
public LegacyService(boolean enableLogging) {
228
this.logger = enableLogging ? LoggerFactory.getLogger(getClass()) : null;
229
}
230
231
// Better approach using @CheckForNull:
232
@CheckForNull
233
public String getDebugInfo() {
234
return logger != null ? logger.getName() : null;
235
}
236
237
public void logMessage(@NonNull String message) {
238
// Handle possibly null logger
239
if (logger != null) {
240
logger.info(message);
241
}
242
}
243
}
244
```
245
246
### ReturnValuesAreNonnullByDefault Annotation
247
248
Indicates that methods in the annotated scope have nonnull return values by default.
249
250
```java { .api }
251
/**
252
* This annotation can be applied to a package, class or method to indicate that
253
* the methods in that element have nonnull return values by default unless
254
* there is:
255
* <ul>
256
* <li>An explicit nullness annotation
257
* <li>The method overrides a method in a superclass (in which case the
258
* annotation of the corresponding parameter in the superclass applies)
259
* <li>there is a default annotation applied to a more tightly nested element.
260
* </ul>
261
*/
262
@Documented
263
@Nonnull
264
@TypeQualifierDefault(ElementType.METHOD)
265
@Retention(RetentionPolicy.RUNTIME)
266
@interface ReturnValuesAreNonnullByDefault {
267
}
268
```
269
270
**Usage Examples:**
271
272
```java
273
// Applied to class - all methods return non-null by default
274
@ReturnValuesAreNonnullByDefault
275
public class UserService {
276
277
// Returns non-null by default
278
public String getUserName(String userId) {
279
String name = repository.findNameById(userId);
280
return name != null ? name : "Unknown"; // Must ensure non-null return
281
}
282
283
// Explicit annotation overrides default
284
@Nullable
285
public User findUser(String userId) {
286
return repository.findById(userId); // May return null
287
}
288
289
// Still non-null by default
290
public List<User> getAllUsers() {
291
List<User> users = repository.findAll();
292
return users != null ? users : Collections.emptyList();
293
}
294
}
295
296
// Can also be applied to packages in package-info.java:
297
@ReturnValuesAreNonnullByDefault
298
package com.example.service;
299
```
300
301
## Null Safety Best Practices
302
303
1. **Use @NonNull by default**: Start with the assumption that references should not be null
304
2. **Explicit @Nullable**: Only use @Nullable when null is a valid, expected value
305
3. **Check before use**: Always check @Nullable and @CheckForNull values before dereferencing
306
4. **Document null handling**: Use clear parameter and return value documentation
307
5. **Consistent application**: Apply annotations consistently across your codebase
308
6. **Default annotations**: Use @ReturnValuesAreNonnullByDefault to reduce annotation overhead
309
310
## Integration with Other Tools
311
312
These annotations work with:
313
- **SpotBugs**: Primary static analysis integration
314
- **NullAway**: Uber's null safety checker
315
- **Checker Framework**: Advanced static analysis
316
- **IDEs**: IntelliJ IDEA, Eclipse null analysis
317
- **Error Prone**: Google's static analysis tool