Monday, April 29, 2019

A New Era for Determining Equivalence in Java?

Liam Miller-Cushon has published a document simply called "Equivalence" in which he proposes "to create a library solution to help produce readable, correct, and performant implementations of equals() and hashCode()." In this post, I summarize some reasons why I believe this proposal is worth reading for most Java developers even if the proposal never gets implemented and why the proposal's implementation would benefit all Java developers if realized.

Miller-Cushon opens his proposal with a single-sentence paragraph: "Correctly implementing equals() and hashCode() requires too much ceremony." The proposal points out that today's powerful Java IDEs do a nice job of generating these methods, but that there is still code to be read and maintained. The proposal also mentions that "over time these methods become a place for bugs to hide." I have been on the wrong end more than once of particularly insidious bugs caused by an error in one of these methods and these can be tricky to detect.

All three editions of "Effective Java" provide detailed explanation and examples for how to write effective implementations of these methods, but it's still easy to get them wrong. The JDK 7 (Project Coin)-introduced methods Objects.equals(Object, Object) and Objects.hash(Object...) have helped considerably (especially in terms of readability and dealing with nulls properly), but there are still errors made in implementations of Object.equals(Object) and Object.hashCode().

Even if this "Equivalence" proposal never comes to fruition, there is some value in reading Miller-Cushon's document. One obvious benefit of this document is its capturing of "Examples of bugs in equals and hashCode implementations." There are currently nine bullets in this section describing the "wide array of bugs in implementations of equals and hashCode methods" that were often identified only when "static analysis to prevent these issues" was performed. These examples serve as a good reminder of the things to be careful about when writing implementations of these methods and also reminds us of the value of static analysis (note that Miller-Cushon is behind the static analysis tool error-prone).

Reading of the "Equivalence" document can also be enlightening for those wanting to better understand the related issues one should think about when developing the equivalence concept in Java. Through sets of questions in the "Requirements" and "Design Questions" sections, the document considers trade-offs and implementation choices that would need to be made. These cover topics such as how to handle nulls, instanceof versus getClass(), and the relationship to Comparator. Many of these considerations should probably be made today by Java developers implementing or maintaining their own implementations of equals(Object) and hashCode().

The "Related reading" section of the "Equivalence" document provides links to some interesting reading that includes the 2009 classic article "How to Write an Equality Method in Java" and RĂ©mi Forax's ObjectSupport class (which delegates to ObjectSupports in some cases).

The "Equivalence" proposal was presented on the OpenJDK amber-spec-experts mailing list in a post title "A library for implementing equals and hashCode" and some of the feedback on that mailing list has led to updates to the document. One particularly interesting sentence for me in this discussion is Brian Goetz's statement, "That people routinely implement equals/hashCode explicitly is something we would like to put in the past." That seems like a welcome change!

1 comment:

@DustinMarx said...

Peter Verhas's post "A New Era for Determining Equivalence in Java?" adds more context to the discussion covered here based on his experiences working with these concepts on a project.