Generating equals(..), hashCode() and toString()
You most probably need to override hashCode()
, equals(..)
and toString()
– I won’t go into details when and why, but you need that (ok, just a reminder – always implement hashCode and equals together, and you most likely need to implement these methods if you are going to look up objects of a given class in a hashmap or an arraylist). And you have plenty of options to do it:
- Manually implement the methods – that’s sort-of ok for toString() and quite impractical with
hashCode()
and equals(..)
. Unless you are pretty certain that you want a custom, well-considered hash function, then you should rely on another, more practical mechanism
- Use the IDE – all IDEs can generate the three methods, asking you to specify the fields you want to base them on. The hash function is usually good enough, and the rest just saves you from the headache of writing boilerplate comparisons, ifs and elses. But when you add a field, you shouldn’t forget to regenerate the methods.
- commons-lang – there’s
EqualsBuilder
, HashCodeBuilder
and ToStringBuilder
there, which help you write the methods quickly, either with manual append(field).append(field)
, or with reflection, e.g. reflectionEquals(..)
. Adding a field again requires modifications, and it’s easy to forget that.
- guava – very similar to commons-lang, with all the pros and cons. Guava has
Objects
and MoreObjects
, with helper functions for equals(..) and hashCode and a builder for toString() – you still have to manually add/compare each field you want to include.
- project lombok – it plugs into the compiler and turns some annotations into actual implementations, sparing you writing the biolerplate code completely. For example, if you annotated the class with
@EqualsAndHashCode
, Lombok will generate the two methods with all the fields in the class (you can customize that). The other annotations are @ToString
, @Value
(for immutables), @Data
(for value-objects). You just have to put a jar on your compile time classpath, and it should work.
Which of these should you use? I generally exclude the manual approach, as well as guava and commons-lang – they require too much manual work for a task that you shouldn’t need to care in 99% of the cases. The reflection option with commons-lang sounds interesting, but also sounds like performance overhead.
I’ve always used the IDE – the only downside of this is that you have to regenerate them. Sometimes you may forget and that may yield unexpected behaviour. But apart from that, it’s quick and robust approach.
Project lombok seems to eliminate the risk of forgetting to regenerate, but that sometimes has another side effect – you may not need to automatically include all new fields, and you can forget to exclude them. But my personal reluctance to use lombok is based on a sort-of a superstition – it does “black magic” by plugging into the compiler. It does work, but it you don’t know how exactly it manages to handle both eclipse compiler, javac, IntelliJ compiler; will it always work with maven, including your CI environment? Will it work through a major/minor compiler version upgrade? Obviously it does, and I have no rational argument against it. And it has some more useful features as well.
So, it’s up to you to pick either of the two approaches. But do not implement it manually, and I don’t think the helper functions/builders are that practical.
You most probably need to override hashCode()
, equals(..)
and toString()
– I won’t go into details when and why, but you need that (ok, just a reminder – always implement hashCode and equals together, and you most likely need to implement these methods if you are going to look up objects of a given class in a hashmap or an arraylist). And you have plenty of options to do it:
- Manually implement the methods – that’s sort-of ok for toString() and quite impractical with
hashCode()
andequals(..)
. Unless you are pretty certain that you want a custom, well-considered hash function, then you should rely on another, more practical mechanism - Use the IDE – all IDEs can generate the three methods, asking you to specify the fields you want to base them on. The hash function is usually good enough, and the rest just saves you from the headache of writing boilerplate comparisons, ifs and elses. But when you add a field, you shouldn’t forget to regenerate the methods.
- commons-lang – there’s
EqualsBuilder
,HashCodeBuilder
andToStringBuilder
there, which help you write the methods quickly, either with manualappend(field).append(field)
, or with reflection, e.g.reflectionEquals(..)
. Adding a field again requires modifications, and it’s easy to forget that. - guava – very similar to commons-lang, with all the pros and cons. Guava has
Objects
andMoreObjects
, with helper functions for equals(..) and hashCode and a builder for toString() – you still have to manually add/compare each field you want to include. - project lombok – it plugs into the compiler and turns some annotations into actual implementations, sparing you writing the biolerplate code completely. For example, if you annotated the class with
@EqualsAndHashCode
, Lombok will generate the two methods with all the fields in the class (you can customize that). The other annotations are@ToString
,@Value
(for immutables),@Data
(for value-objects). You just have to put a jar on your compile time classpath, and it should work.
Which of these should you use? I generally exclude the manual approach, as well as guava and commons-lang – they require too much manual work for a task that you shouldn’t need to care in 99% of the cases. The reflection option with commons-lang sounds interesting, but also sounds like performance overhead.
I’ve always used the IDE – the only downside of this is that you have to regenerate them. Sometimes you may forget and that may yield unexpected behaviour. But apart from that, it’s quick and robust approach.
Project lombok seems to eliminate the risk of forgetting to regenerate, but that sometimes has another side effect – you may not need to automatically include all new fields, and you can forget to exclude them. But my personal reluctance to use lombok is based on a sort-of a superstition – it does “black magic” by plugging into the compiler. It does work, but it you don’t know how exactly it manages to handle both eclipse compiler, javac, IntelliJ compiler; will it always work with maven, including your CI environment? Will it work through a major/minor compiler version upgrade? Obviously it does, and I have no rational argument against it. And it has some more useful features as well.
So, it’s up to you to pick either of the two approaches. But do not implement it manually, and I don’t think the helper functions/builders are that practical.
Nice overview 🙂
There’s also Google AutoValue
https://github.com/google/auto/tree/master/value
It uses a similar approach as Lombok but only uses the standard annotation processing APIs. Which brings some limitations: The value class has to be abstract and must have a static factory method instead of a public constructor.
Also there’s only a release candidate for now.
I wish we had something like Scala’s case classes in Java, though… 95% of all POJOs (that require equals, hashCode, and toString) would indeed be modelled as case classes.
Yes, case classes are nice, although they have one limitation – you need to specify all parameters in the constructor, and of some of them are optional, use Option[..], and that’s not a typical Java approach.
I dislike the IDE generated methods as it affects our code coverage statistics. Not only is the code complex, it’s painful to get 100% coverage on the methods. I like to use the Apache commons-lang. Simple and easy to use.
Java 8 introduces some options for statically defining field accessors you can pass to utility methods for processing.
Example here: http://illegalargumentexception.blogspot.co.uk/2014/08/java-easy-equalshashcodetostring-less.html
The results are still noisier than desirable but you can avoid repeated overheads like autoboxing, vararg arrays, reflection or pre-processors.
Also, it may be worth adding java.util.Objects to your list for those sticking to the standard APIs.
Lombok is great, I used a lot when I was doing Java Web and loved it.
It also has the delombok tool just in case you don’t want to rely on the “black magic” anymore, so that helps reducing the fear a little bit. =)
I would rather not have 100% coverage, than having to manually write biolerplate code 🙂
The Java 8 approach is similar to the guava one, and I still think it’s too much manual work.
This entails plumbing work, fixing electrical fittings, and providing various accessories for maximum pleasure and comfort.
These scales are far more accurate and they look elegant and modern in contrast to the normal weighing scale,
which you discover that are available in the market.
This will help you become aware of the foods that you eat and the way in which you
eat them.
How about defining a id field (which will always be unique) and simply use that in equal method in combination with IDE generated methods. That way you don’t have to regenerate methods if you add a field. You can simply check against this.id.equals(other.id) after doing usual checks of null/instance etc. and not worry about rest of the fields since your id is going to be unique. To generate unique there are many common ways include uuid and ThreadLocalRandom.current.next() etc.
I also looked at Project Lombok but deemed it too risky to use (f.x. there are issues with the latest JDK). AutoValue is pretty but I also found the customisation features lacking (also a problem with Lombok), so I just released my own open source tool for generating java value objects that used 100% standard java features and is extremely customisable. You can modify and extend the value objects with extra generated functionality. Want your own cached hashCode or a custom generated method method inserted. No problem. It also does not require you to extend any base class so there are no constrains on how the implementation will look like. Finally, it has build in support for additional methods like compareTo You can check it out at “http://valjogen.41concepts.com”. Let me know what you think?