22

Implementing equals() and hashCode() for simple data POJOs is cluttering my code and maintaining is tedious.

What are the libraries handling this automatically?
I prefer bytecode instrumentation over AOP approach due to performance reasons.

Update: The topic of the necessity of implementing equals() and hashCode() has been discussed, here's my point:

Isn't it better to have it done right upfront with minimal effort rather than digging in the code, adding hC/eq when it comes to it?

Edit 2022: I have switched to Kotlin. Kotlin takes care of most of Java's boilerplate, see this page for the case of equals(): https://tedblob.com/kotlin-data-class/

Ondra Žižka
  • 43,948
  • 41
  • 217
  • 277
  • 5
    No library can take care of `equals` for you - only you know what makes two objects equal. What IDE are you using that doesn't generate `hashCode` for you? And why are you having to do so much maintenance on `equals` and `hashCode`? – Paul Aug 05 '11 at 16:12
  • 1
    I understand that in the Java community it's common to implement these methods for basically every POJO, but I have to ask: are you actually using all of your POJO types as keys in hash tables? Are you comparing them for equality? If not, then why even bother? – Dan Tao Aug 05 '11 at 16:26
  • @Paul - NetBeans, and it generates; only, I have to re-generate every time, and also it makes 10's of lines of code. Also, the lib could use reflection when generating, so why couldn't it handle equals? – Ondra Žižka Aug 05 '11 at 16:54
  • @Dan - well, I don't, but various frameworks do - e.g. web frameworks. Keeping track of what instance gets where and when may be difficult in an app with many paradigms used - with all that caching, injection, serialization in components between requests... – Ondra Žižka Aug 05 '11 at 16:56
  • @OndraŽižka: Java's expressiveness is limited by the lack of any declarative distinction between a reference which encapsulates the *state* of an unshared mutable or sharable immutable object, and one which encapsulates the *identity* of a shared object. References which are used to encapsulate state should match if the *states* of the identified objects match. By contrast, those which encapsulate identity should only match if they identify the *same* object. To define an equivalence relation, one must know the purpose of the things being compared. – supercat Dec 06 '13 at 17:48
  • How can you *"prefer bytecode instrumentation over AOP approach"* when AOP is based on bytecode instrumentation? Did you mean compile-time vs load-time instrumentation? – Vlastimil Ovčáčík Feb 11 '16 at 18:27
  • 1
    @VlastimilOvčáčík Yes, I think it's clear from the context: I prefer Lombok way rather than AOP way. – Ondra Žižka Feb 28 '18 at 15:54

6 Answers6

16

Project Lombok provides the annotation @EqualsAndHashCode which will generate equals() and hashCode() for your Java classes. Of course there are some drawbacks in comparison to manually implementing these methods, so make sure you read the "small print" on the linked page.

joschi
  • 12,746
  • 4
  • 44
  • 50
6

Objects.hashCode & Objects.hash

While not the panacea you requested, writing the hashCode override is a bit easier now as of Java 7 and later.

As of Java 7, the Objects class offers a couple of utility methods for generating hash code values.

See my Answer on a related Question for more discussion.

Single member, not tolerating a NULL

@Override
public int hashCode() {
    return this.member.hashCode() ;  // Throws NullPointerException if member variable is null.
}

Single member, tolerating a NULL

@Override
public int hashCode() {
    return Objects.hashCode( this.member ) ;  // Returns zero (0) if `this.member` is NULL, rather than throwing exception.
}

Multi-member

@Override
public int hashCode() {
    return Objects.hash( this.memberA , this.memberB , this.memberC  ) ;  // Hashes the result of all the passed objects’ individual hash codes.  
}

For an automatically generated hashCode & equals use a record, as discussed in my other Answer.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
3

You can use Google's AutoValue library to automatically generate immutable value classes with equals and hashCode. These value classes are somewhat similar to Scala's case classes or those generated by Lombok.

There's also a post on how to use it in an IDE.

Andrejs
  • 26,885
  • 12
  • 107
  • 96
3

What about Guava's Objects.hashCode and Objects.equal?

domids
  • 515
  • 5
  • 21
KARASZI István
  • 30,900
  • 8
  • 101
  • 128
1

The Apache commons-lang library has a HashCodeBuilder and EqualsBuilder that will do some of the work for you and shorten those methods. There are even reflection versions that will do it all for you based on the fields in the POJOs. However, I wouldn't recommend that. Reflection can be slow (though not as bad as many think), and you should implement them to be sure that only the correct fields are considered for equality.

My question is, do you really need to do this? Often hashcode and equals on POJOs only need to be implemented for use with Maps or Sets. In the case of Maps, usually you would use an ID for a key, which isn't the Pojo itself. So, .... are you making work for yourself?

rfeak
  • 8,124
  • 29
  • 28
  • +1 for 2nd paragraph, but isn't it better to have it done right upfront with minimal effort rather than digging in the code, adding hC/eq when it comes to it? – Ondra Žižka Aug 05 '11 at 17:16
  • Like everything, it ends up being a trade off. It's nice if it's done up front, but implementing it when actually needed hasn't proven overly difficult in my experience. Of course, I usually have tests that tell me when I missed implementing it. :) – rfeak Aug 05 '11 at 19:43
0

Records

Java 16 gained a new feature, records.

If the main purpose of your class is to communicate data transparently and immutably, write the class as a record.

By default, you need do nothing more than declare the member fields. The compiler implicitly creates the constructor, getters, equals & hashCode, and toString.

record Person ( String givenName , String surname ) {}

record Point ( int x , int y ) {}

record User ( UUID id, String name ) {}

The default behavior of a record’s equals & hashCode is to consider each and every member field. You can override to provide your own logic.

I highly recommend reading the Java JEP linked above. You’ll learn that the purpose of the record feature is not a reduction in boilerplate. Less code is a nice extra benefit. But if you choose record only for less typing, you’ll likely be abusing the feature. Such abuse may confuse people reading your code, and may lead to other problems.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154