Skip to content

Commit c78d87f

Browse files
cpovirkError Prone Team
authored andcommitted
Document an alternative to overriding Throwable.equals.
PiperOrigin-RevId: 900744432
1 parent cc35ba1 commit c78d87f

1 file changed

Lines changed: 35 additions & 0 deletions

File tree

docs/bugpattern/ThrowableEqualsHashCode.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,38 @@ Throwables should not override `equals()` or `hashCode()`.
3131
Exceptions for normal business logic or control flow, which is a known
3232
anti-pattern. Exceptions are for exceptional circumstances; they are heavy
3333
(because of the stack trace) and slow to generate.
34+
35+
### **Recommended Alternative: A separate value object**
36+
37+
If you find yourself needing to compare exceptions, **extract the state into a
38+
separate value object.**
39+
40+
Instead of this:
41+
42+
```java
43+
public class UserNotFoundException extends RuntimeException {
44+
private final String userId;
45+
private final String groupId;
46+
47+
// Override equals and hashCode based on userId and groupId...
48+
}
49+
```
50+
51+
Do this: Create a custom Exception that *contains* a value object (like a Record
52+
or a POJO), and compare the value objects instead.
53+
54+
```java
55+
public class UserNotFoundException extends RuntimeException {
56+
private final ErrorDetails details;
57+
58+
public UserNotFoundException(ErrorDetails details) {
59+
super("%s is not a member of %s:".formatted(details.userId(), details.groupId()));
60+
this.details = details;
61+
}
62+
63+
public ErrorDetails getDetails() { return details; }
64+
}
65+
66+
// Compare the details, not the exceptions!
67+
if (e1.getDetails().equals(e2.getDetails())) { ... }
68+
```

0 commit comments

Comments
 (0)