Skip to content

Comparer

Tomasz Kuryłek edited this page Jul 8, 2013 · 9 revisions

Sometimes you want to compare two objects in a specific way. Simple and very common problem. When you don't want to override Object.equals() method comparer is a way to go.

The package comparer consists of two intefaces:

  • Comparer - with method areEqual that determines whether two objects are the same or not.
  • Comparable - with no methods. It marks objects that can be compared by comparer.

Comparing by specified field

Let's say we want to compare three Hero objects. Each hero has it's super power. We want to compare heroes just by looking at their super powers.

Hero hulk = aHulk().withSuperPower(STRENGTH).build();
Hero captainAmerica = aCaptainAmerica().withSuperPower(STRENGTH).build();
Hero ironMan = anIronMan().withSuperPower(ARMOR).build();

As you can see we have hulk and captainAmerica that share the same super power - strength, and ironMan with it's armor.
You could write a comparer that only compares heroes and it would look like this:

Comparer<Hero> comparer = new Comparer<Hero>() {
   @Override
   public boolean areEqual(Hero first, Hero second) {
      return first.getSuperPower().equals(second.getSuperPower());
   }
};
comparer.areEqual(hulk, captainAmerica); // true
comparer.areEqual(captainAmerica, ironMan); // false

But what if we have another class Malefactor and an object redScull whose super power is also strength? We want to be able to compare malefactors with heroes as well.

Malefactor redScull = aRedScull().withSuperPower(STRENGTH).build();

This is when you want to create an interface ComparableBySuperPower that extends Comparable like this:

public interface ComparableBySuperPower extends Comparable {
    SuperPower getSuperPower();
}

Then make sure that Hero and Malefactor classes are implementing that interface. Take a look at the example Hero class that implements ComparableBySuperPower properly.

public class Hero implements ComparableBySuperPower {
   private Long id;
   private String nickname;
   private SuperPower superpower;
   @Override
   public SuperPower getSuperPower() {
      return superpower;
   }
}

Then create a new comparer:

public class CompererBySuperPower<T extends ComparableBySuperPower> implements Comparer<T> {
    @Override
    public boolean areEqual(T first, T second) {
	return first.getSuperPower().equals(second.getSuperPower());
    }
}

Now you can compare heroes and malefactors (and any other class that implements ComparableBySuperPower) by their super powers.

Comparer<ComparableBySuperPower> comparerBySuperPowers = new CompererBySuperPowers<>();
comparerBySuperPowers.areEqual(hulk, redScull); // true
comparerBySuperPowers.areEqual(redScull, ironMan); // false

ComparerById

When working with object-relational mapping you often experience that the lazy loading has its pros and cons. Let's say you want to compare two objects that have been just loaded from the database. You often will see that only one field (usually ID) of each object is initialized and the rest will be fetched when you need them. Because of lazy loading comparing two objects using equals can be really painful for the database, but comparing them by their special, unique field can be a real time saver. With ComparerById it's easy. Your domain objects need to implement ComparableById with it's getId() method and you're off.
So let's say we have two Hero objects with getId() methods.

Hero hulk = aHulk().withId(ID).build();
Hero captainAmerica = aCaptainAmerica().withId(SAME_ID).build();

And we want to compare them by their ids. Using comparer it would look like this:

Comparer<Hero> heroesComparerById = new CompererById<>(); // create a comparer for Hero objects
boolean result = heroesComparerById.areEqual(hulk, captainAmerica); // compare two heroes

Clone this wiki locally