What are ways to keep hashCode/equals consistent with the business definition of the class?
Object javadocs and Josh Bloch tell us a great deal about how hashCode/equals should be implemented, and good IDEs will handle fields of various types correctly. Some discussion about all that is here.
This question is about the next step: How do you make sure that they remain good?
In particular, I feel that for most Classes, equals/hashCode should be implemented as Bloch suggests (and Eclipse and other IDE's implement), and take in to account all non-derived, business-logic, fields on that class. While adding new fields to a class as part of continuing work, people often forget to add them to the equals/hashCode implementation. This can lead to hard-to-find bugs, when two objects appe开发者_如何学Pythonar equal, but in fact differ by the value of a recently introduced field.
How can a team (even of one!) help ensure that the equals/hashCode on a class continue to take in to account all relevant fields, as the member fields change?
I know that Apache's EqualsBuilder and HashCodeBuilder can use reflection, which obviously would take in to account the correct fields, but I want to avoid the performance costs of using them. Are there other approaches to flag up fields that are not included in equals/hashCode, and should be? Static code analysis, IDE features, unit test techniques?
Never tried it, but how about http://code.google.com/p/equalsverifier/?
A potential answer seems to be offered in this question.
I haven't looked into Project Lombok much, but I immediately thought, hmm annotations would work with a code generator.
How about you write unittests for each class you want to protect. The unit tests should
- Use reflection to count the number of fields, and compare with locally stored field names (and types).
- If a change in number of fields (or type of fields) has been detected, then invoke hashcode. If the value is the same, then fail the test case. Alert the developer that fields of a class have been changed, and that the developer should : Update the equals and Hashcose, and update the unittest.
- Make sure the unittest runs in a nightly build.
You don't need to include every field in your hash code method. If you used more than one field in your hash code algorithm then the chances are it will remain good. The IDE-generated algorithms I've seen use a random (at the time of implementation, not at execution) prime constant value, so just make sure that classes that could end up in the same map or tree together (e.g. they implement the same interface) have different constant values. Unless, of course, you want equality at the interface level.
I've never seen a hash code algorithm "go bad" - you're worrying a disproportionate amount about this.
You could serialize your objects to a string using a tool that finds your properties using reflection (XStream, for example), and store that string in a repository. Your unit tests could re-serialize your objects and compare the results to your stored values.
As part of your own process, make the storage of those strings contingent on manually validating that your hashCode and equals correctly capture all relevant values.
Sounds like what you're looking for is an addon or feature in an IDE that performs the analysis of the classes and generates warnings if the equals() and hashCode() methods do not reference all the relevant fields.
This would basically move the reflection overhead to the IDE rather than let it have the cost during run-time.
Unfortunately, I don't know of any addon that does this, though I'll certainly have a look.
On the other hand, how would such an automated tool know which fields are relevant for business logic? You'd probably need to mark irrelevant fields with something like an annotation, otherwise you could find yourself with warnings you can't get rid of, but that you know are incorrect.
Edit: This answer is not useful, so I'm just compiling here the really useful suggestions from better answers:
- Project Lombok - Eclipse addon that also works with build tools to provide automatic generation of
equals()
,hashCode()
and other boiler-plate code - since it's generated at compile-time, any changes to the class will automatically update these methods too - equalsverifier - Provides a way to automatically unit test
equals()
with reflection - doesn't support testinghashCode()
精彩评论