0
# Return Value Checking
1
2
Enforce that method return values are checked by callers to prevent ignored error conditions and resource leaks. This is particularly important for methods that return status codes, validation results, or resource handles.
3
4
## Capabilities
5
6
### CheckReturnValue Annotation
7
8
Marks methods whose return values should always be checked when invoking the method.
9
10
```java { .api }
11
/**
12
* This annotation is used to denote a method whose return value should always
13
* be checked when invoking the method.
14
*
15
* The checker treats this annotation as inherited by overriding methods.
16
*/
17
@Documented
18
@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR })
19
@Retention(RetentionPolicy.CLASS)
20
@interface CheckReturnValue {
21
22
/**
23
* @deprecated Use confidence() instead
24
*/
25
@Deprecated
26
Priority priority() default Priority.MEDIUM;
27
28
/**
29
* The confidence level for this check
30
*/
31
Confidence confidence() default Confidence.MEDIUM;
32
33
/**
34
* A textual explanation of why the return value should be checked
35
*/
36
String explanation() default "";
37
}
38
```
39
40
**Usage Examples:**
41
42
```java
43
public class ConnectionManager {
44
45
// Return value must be checked - connection might fail
46
@CheckReturnValue(explanation = "Connection status must be verified before use")
47
public boolean connect() {
48
try {
49
establishConnection();
50
return true;
51
} catch (Exception e) {
52
return false;
53
}
54
}
55
56
// File operations should check success status
57
@CheckReturnValue(explanation = "File operations may fail and should be verified")
58
public boolean saveToFile(@NonNull String filename, @NonNull String data) {
59
try {
60
Files.write(Paths.get(filename), data.getBytes());
61
return true;
62
} catch (IOException e) {
63
logger.error("Failed to save file: " + filename, e);
64
return false;
65
}
66
}
67
68
// Resource creation must be checked
69
@CheckReturnValue(explanation = "Resource acquisition may fail")
70
public InputStream openResource(@NonNull String resourcePath) {
71
return getClass().getResourceAsStream(resourcePath); // May return null
72
}
73
74
// Validation results must be examined
75
@CheckReturnValue(explanation = "Validation errors must be handled")
76
public ValidationResult validateInput(@NonNull String input) {
77
return validator.validate(input);
78
}
79
}
80
81
// Proper usage - checking return values
82
public class ServiceClient {
83
84
public void performOperation() {
85
ConnectionManager manager = new ConnectionManager();
86
87
// GOOD: Check return value
88
boolean connected = manager.connect();
89
if (!connected) {
90
throw new RuntimeException("Failed to connect");
91
}
92
93
// GOOD: Check file operation result
94
boolean saved = manager.saveToFile("output.txt", "data");
95
if (!saved) {
96
logger.warn("Failed to save output file");
97
}
98
99
// GOOD: Check resource acquisition
100
InputStream stream = manager.openResource("/config.properties");
101
if (stream == null) {
102
throw new RuntimeException("Configuration file not found");
103
}
104
105
// GOOD: Handle validation results
106
ValidationResult result = manager.validateInput(userInput);
107
if (!result.isValid()) {
108
displayErrors(result.getErrors());
109
return;
110
}
111
}
112
113
public void badUsage() {
114
ConnectionManager manager = new ConnectionManager();
115
116
// BAD: Ignoring return value - SpotBugs will warn
117
manager.connect(); // Warning: return value should be checked
118
119
// BAD: Not checking file operation
120
manager.saveToFile("output.txt", "data"); // Warning: return value ignored
121
}
122
}
123
```
124
125
### Constructor Return Value Checking
126
127
The annotation can also be applied to constructors to indicate that construction success should be verified.
128
129
```java { .api }
130
public class ResourceHandle {
131
132
// Constructor that may fail - check for null or use factory method
133
@CheckReturnValue(explanation = "Resource creation may fail")
134
public ResourceHandle(@NonNull String resourceId) {
135
if (!isValidResource(resourceId)) {
136
throw new IllegalArgumentException("Invalid resource: " + resourceId);
137
}
138
// Initialize resource
139
}
140
141
// Factory method with return value checking
142
@CheckReturnValue(explanation = "Resource creation may fail, returns null on failure")
143
public static ResourceHandle tryCreate(@NonNull String resourceId) {
144
try {
145
return new ResourceHandle(resourceId);
146
} catch (Exception e) {
147
return null; // Caller must check for null
148
}
149
}
150
}
151
152
// Usage
153
public void createResource() {
154
// GOOD: Using exception-based constructor (automatic checking)
155
try {
156
ResourceHandle handle = new ResourceHandle("resource-123");
157
useResource(handle);
158
} catch (IllegalArgumentException e) {
159
logger.error("Failed to create resource", e);
160
}
161
162
// GOOD: Checking factory method return value
163
ResourceHandle handle = ResourceHandle.tryCreate("resource-456");
164
if (handle != null) {
165
useResource(handle);
166
} else {
167
logger.error("Failed to create resource");
168
}
169
}
170
```
171
172
### Method Overriding and Inheritance
173
174
The `@CheckReturnValue` annotation is inherited by overriding methods.
175
176
```java { .api }
177
public abstract class BaseService {
178
179
@CheckReturnValue(explanation = "Service operations may fail")
180
public abstract boolean performOperation();
181
}
182
183
public class DatabaseService extends BaseService {
184
185
// Inherits @CheckReturnValue from parent method
186
@Override
187
public boolean performOperation() {
188
try {
189
executeQuery();
190
return true;
191
} catch (SQLException e) {
192
return false;
193
}
194
}
195
}
196
197
// Usage - must check return value even when calling through subclass
198
public void useService() {
199
DatabaseService service = new DatabaseService();
200
201
// GOOD: Checking inherited return value requirement
202
boolean success = service.performOperation();
203
if (!success) {
204
handleFailure();
205
}
206
207
// BAD: Ignoring return value - SpotBugs will warn
208
service.performOperation(); // Warning: return value should be checked
209
}
210
```
211
212
### Integration with Confidence Levels
213
214
Use confidence levels to indicate how important return value checking is.
215
216
```java { .api }
217
public class SecurityService {
218
219
// High confidence - critical security operation
220
@CheckReturnValue(
221
confidence = Confidence.HIGH,
222
explanation = "Authentication failures must be handled"
223
)
224
public boolean authenticate(@NonNull String username, @NonNull String password) {
225
return authProvider.validate(username, password);
226
}
227
228
// Medium confidence - important but not critical
229
@CheckReturnValue(
230
confidence = Confidence.MEDIUM,
231
explanation = "Cache operations should be verified"
232
)
233
public boolean cacheData(@NonNull String key, @NonNull Object data) {
234
return cache.put(key, data);
235
}
236
237
// Low confidence - nice to check but not essential
238
@CheckReturnValue(
239
confidence = Confidence.LOW,
240
explanation = "Logging failures are typically non-critical"
241
)
242
public boolean logEvent(@NonNull String event) {
243
return logger.log(event);
244
}
245
}
246
```
247
248
## Common Use Cases
249
250
### Status Code Returns
251
252
```java
253
@CheckReturnValue(explanation = "HTTP status must be verified")
254
public int makeHttpRequest(@NonNull String url) {
255
// Returns HTTP status code
256
return httpClient.get(url).getStatusCode();
257
}
258
```
259
260
### Resource Acquisition
261
262
```java
263
@CheckReturnValue(explanation = "File handle may be null if file doesn't exist")
264
public FileInputStream openFile(@NonNull String filename) {
265
try {
266
return new FileInputStream(filename);
267
} catch (FileNotFoundException e) {
268
return null;
269
}
270
}
271
```
272
273
### Validation Results
274
275
```java
276
@CheckReturnValue(explanation = "Validation errors must be handled")
277
public boolean isValidEmail(@NonNull String email) {
278
return email.matches("^[A-Za-z0-9+_.-]+@(.+)$");
279
}
280
```
281
282
### Lock Acquisition
283
284
```java
285
@CheckReturnValue(explanation = "Lock acquisition may fail or timeout")
286
public boolean tryLock(long timeout, TimeUnit unit) {
287
try {
288
return lock.tryLock(timeout, unit);
289
} catch (InterruptedException e) {
290
Thread.currentThread().interrupt();
291
return false;
292
}
293
}
294
```
295
296
## Best Practices
297
298
1. **Use meaningful explanations**: Provide clear reasons why the return value should be checked
299
2. **Apply to failure-prone operations**: Focus on methods that can fail or return error indicators
300
3. **Consider inheritance**: Remember that annotations are inherited by overriding methods
301
4. **Use appropriate confidence levels**: Match confidence to the criticality of checking the return value
302
5. **Document alternatives**: If return value checking isn't possible, document exception-based alternatives
303
6. **Consistent application**: Apply consistently across similar methods in your API