Guava's Equivalence: strategy for equals()/hashCode()


Given a class C, if you want to implement ordering, you have two choices:

  1. Have your class implement Comparable<c> (or Comparable<x>, where X is a superclass of C).
  2. Define a Comparator<c> to use with this class (or a Comparator<x>, where X is a superclass of C).

In fact, many classes in the JDK will have you supply a Comparator if your class does not implement Comparable; examples include Collections.sort() and Arrays.sort().
It can be said that for a given class C, a Comparator defines a strategy for ordering, and that you need to supply a Comparator if the class itself does not define this strategy (that is, does not implement Comparable).
And while the JDK offers a means to provide different strategies for ordering, it does not do so for a more fundamental contract of Object: equals() and hashCode().
And this is where Guava comes in.

Equivalence: a strategy for Object’s `equals()` and `hashCode()`

Guava’s Equivalence intends to fill this gap. In the same vein as a Comparator, using an Equivalence allows you to either define a different equals()/hashCode() strategy than the one already defined by your target class, or to define one for a target class which “has none at all” (meaning, in this case, that the class uses Object‘s equals()/hashCode() implementations).

Usage part 1: implementing an Equivalence

Equivalence is a parameterized class; for a class C, you will need to implement two methods:

  • doEquivalent(C a, C b): given two instances a and b of class C, are those two classes considered equivalent? This is really like writing an implementation of equals() for class C, except that you don’t have to handle nulls (it is guaranteed that both parameters are non-null) nor casts (it is guaranteed that both arguments are “at least” of type C).
  • doHash(C t): given an instance t of class C, compute a hash value. Of course, an implementation must mirror Object‘s hashCode()/equals() contract: if doEquivalent(a, b) is true, then doHash(a) == doHash(b).

Note that it is guaranteed that arguments to these methods will never be null.

Usage part 2: “out of Collections” usage

There is really only one method you will need here: .equivalent(). Provided you want to test equivalence between two instances of a given class C, you will do:

final C a = ...;
final C b = ...;
final Equivalence<C> eq = ...;
// Test equivalence of a and b against equivalence stragey eq
if (eq.equivalent(a, b)) {
    // Yup, they are

Usage part 3: Collection usage

Unlike the Comparable/Comparator relationship, equivalence between objects has to be “engraved” into collections. This unfortunately means that the syntax to initiate a collection is somewhat on the verbose side. Namely, if you want to initiate a Set of elements of class C wrapped into an Equivalence, you will have to initialize it as such:

// Java 7 and better...
Set<Equivalence.Wrapper<C>> set = new HashSet<>();
// Java 5 or 6; and since we use Guava...
Set<Equivalence.Wrapper<C>> set = Sets.newHashSet();

You will also need to rely on an Equivalence implementation in order to interact with this collection (of course, you also need to ensure that you use the same implementation all along!):

// Your Equivalence...
Equivalence<C> eq = ...;
// Inserting an element c into a Set<Equivalence.Wrapper<C>> set
// Removing, testing...
// Retrieve the original element
C element;
for (final Equivalence.Wrapper<C> wrapped: set) {
    // get the original element
    element = wrapped.get();
    // do something with element


Admittedly, having to redefine an equivalence strategy is far less common than having to redefine an ordering strategy. It is, however, a welcome tool to use when you have to deal with a “foreign” class which doesn’t meet your equals()/hashCode() expectations, either because there is no implementation at all for this class, or because the existing implementations don’t suit your needs.
Happy coding!

Catching (and implementing) exceptions


In a previous article on Using exceptions correctly, exception propagation was explained: use runtime exceptions for situations in which the code is incorrect, and use checked exceptions for situations for which there is a valid recovery path internal to the application.
This article deals with two other things you will have to do with exceptions: catching them and implementing them. And in order to do both, you have to know about exception classes themselves…

Java’s Exception Classes

At the core of all exceptions is a class called Throwable. It is the superclass for both Exception and Error. (OutOfMemoryError is an example of an Error.)
The truth of the matter is you should probably never encounter code which directly handles (or throws!) a Throwable which is not also an Exception, but you should know that it exists. You never know: some inexperienced programmer might add it to his or her exception handling, and then you’d have to clean up their code.
The Exception class itself is the base class for all checked exceptions. When you want to create custom exceptions, you will probably want to extend this one (or a more specialized version: IOException, for instance).
The RuntimeException class Is the base class for all unchecked exceptions. If you browse the JDK javadoc, you will notice that all such niceties as the infamous NullPointerException (NPE for short), but also IllegalArgumentException, IndexOutOfBoundsException, etc., are all subclasses of RuntimeException.
But there’s a catch (pun intended): RuntimeException is also a subclass of Exception. This has an often overlooked consequence on catch statements, see below.

Catching, and the importance of exception inheritance

Since exceptions are Java classes like any other, exception classes which you can catch may inherit from other exception classes. This has an influence on how you catch them. For starters…

Catch more specific exceptions first

Let us take an example of a method doing filesystem operations using the java.nio.file API. Such operations can fail at two levels:

  • Filesystem-level errors; the exception defined by the JDK for such errors is FileSystemException.
  • I/O errors; in this case you will probably get an IOException instead.

From the javadoc links above, you may have noticed that FileSystemException inherits IOException. Provided you want to treat both exceptions differently, you must catch FileSystemException first:

try {
} catch (FileSystemException e) {
    // deal with filesystem level errors
} catch (IOException e) {
    // deal with I/O errors

As an added benefit, you get to access all methods defined in FileSystemException (getFile(), etc) which IOException does not define, allowing you to (for instance) construct much more detailed error messages.
If, instead, your code was:

try {
} catch (IOException e) {
    // deal with it
} catch (FileSystemException e) {

As explained, you will never get to deal with FileSystemExceptions separately – the more general IOException will match and its exception-handling block called, instead.

Catching Exception is dangerous…

Remember what was said earlier? RuntimeException inherits Exception. As such, if you simply catch Exception, you also get to catch all unchecked exceptions!
You don’t want to do that, of course… But just in case, if you want to (or, preferrably, need to) have a “one size fits all” catch block, here is an idiom you can use which will “correctly” propagate all unchecked exceptions (reminder: since they are unchecked, you needn’t declare that your method throws them):

try {
    // some exception-heavy code throwing many exceptions
} catch (RuntimeException oops) {
    // Unchecked exception: rethrow!
    throw oops;
// deal with specific exceptions if possible...
// then:
catch (Exception e) {
    // One size fits all

Well, that’s one idiom; a better way to deal with exception-heavy code is to simply reduce the amount of code in your try blocks so that the amount of exceptions you have to deal with is limited.
Alternatively, you can use multicatch.

“Multicatch” (since Java 7)

Editor’s Note: if you’re not on Java 7 by now, you should attempt to upgrade. Java 6 has been end-of-lifed, and it has a support lifecycle that’s not likely to be available for most users. Java 7’s been out for years, people. It’s time.

Again, in the situation where you have to deal with “exception-heavy” try blocks, you have another tool since Java 7 allowing you to treat a given set of exceptions with the same code. So, instead of, for instance, writing:

try {
    // something
} catch (E1 e) {
    // do x with e
} catch (E2 e) {
    // do x with e
// etc

You can instead write:

try {
    // something
} catch (E1 | E2 e) { // | E3 | ...
    // do x with e

Implementing your own exceptions: some rules

Exceptions are regular Java classes. This means that there is more to exception classes than .getMessage(), as we saw with FileSystemException.
Therefore, if you feel the need to, do not hesitate to add methods to your custom exception classes.
However, try to avoid extending Exception directly. The JDK defines plenty of exception classes which can serve as a useful basis for your own exception classes; for instance, if you want to relay an I/O error of your own, you will probably want to extend IOException instead.
Double check whether the exception class you extend inherits RuntimeException… If it does, you have just created an unchecked exception class. Is this what you want?

Side note: implementing “throwing methods”…

When implementing a method which is declared to throw an exception class, the implementation can choose to throw a subclass of this exception. So, for instance, if you have an abstract method to implement declared as:

// Reminder: all declared methods in an interface are "public abstract" by default
void foo() throws SomeException;

… and you have a custom exception MyException which extends SomeException, then you can implement it as such:

// Note the declared exception...
void foo() throws MyException { /* whatever */ }

This is demonstrated by the JDK with AutoCloseable and Closeable: Closeable extends AutoCloseable. The close() method of AutoCloseable is declared to simply throw Exception, whereas its override in Closeable is declared to throw IOException.

Final words…

Hopefully, this article and the quoted article in the introduction should have provided you with enough tools, knowledge and recipes so that you are confident when it comes to dealing with exceptions in Java — whether that be in your own code, or when using other people’s code.
Happy (and fruitful) coding…