Search code examples
javaexceptionnullpointerexceptionequalshashcode

Are exceptions in hashCode and equals methods allowed and acceptable in Java?


Some classes filled by frameworks (like beans). So you can't guaranty that all fields set.

Look to example: classes marked as @Entity usually have Integer id field. hashCode can be written as:

public int hashCode() {
    return id.hashCode();
}

but defensive code may look like:

public int hashCode() {
    return (id != null) ? id.hashCode() : 0;
}

Do I need write checks for null or surround code with try { ... } catch (Exception e) in hashCode and equals functions?

I have no arguments for defencive coding is such case because it hide putting inconsistent objects to collections and lead to late errors. Am I wrong in this position?

UPDATE I wrote such code:

import java.util.*;

class ExceptionInHashcode {

    String name;

    ExceptionInHashcode() { }
    ExceptionInHashcode(String name) { this.name = name; }

    public int hashCode() {
        // throw new IllegalStateException("xxx");
        return this.name.hashCode();
    }

    public static void main(String args[]) {
        Hashtable list = new Hashtable();
        list.put(new ExceptionInHashcode("ok"), 1);
        list.put(new ExceptionInHashcode(), 2); // fail
        System.out.println("list.size(): " + list.size());
    }
}

and run it:

java -classpath . ExceptionInHashcode
Exception in thread "main" java.lang.NullPointerException
        at ExceptionInHashcode.hashCode(ExceptionInHashcode.java:12)
        at java.util.Hashtable.hash(Hashtable.java:262)
        at java.util.Hashtable.put(Hashtable.java:547)
        at ExceptionInHashcode.main(ExceptionInHashcode.java:18)

I think that I can find error early instead of returning zero if object is in wrong state...


Solution

  • In general, the answer is "it depends".

    • If you should never see instances of the class with null for that field, then it is reasonable to allow an NPE to be thrown. The NPE indicates a bug; i.e. a situation where your informal invariant is broken.

    • If there are circumstances where an instance with a null could reasonably expected, then you should deal with the null case without throwing an exception.


    In this particular case, you are apparently dealing with objects where the id field can be null if the object hasn't been persisted yet. This presents a tricky problem:

    • If you don't allow null for the id, then you have to be careful not to put non-persistent objects into a hash table.

    • If you do allow null for the id, then you have the problem that if you add an object to a hash table and THEN persist it, the hashcode may change leading to breakage of the hash table. So, now you need to defend against THAT ... by memoising the object's hashcode value in a transient field. And roughly the same problem occurs with equals. If equality changes when an object is persisted, then you had better not mix persisted and non-persisted keys in the same hash table.

    Taking all of that into account, I'd advise to either throw that NPE, or not use the id fields in equals / hashcode.