How can we instantiate comparator while using it as an argument in sorting?

Viewed 44

Why we use new keyword before comparator while using it as a constructor in sorting as comparator is an interface so we cannot instantiate it?

Collections.sort(persons, new Comparator<Person>() {
  @Override
  public int compare(Person p1, Person p2) {
      return p1.getAge() - p2.getAge();
  }
});
1 Answers

That's because this code does not instantiate Comparator. As you said, that's not possible.

Instead, it is syntax sugar. It's short for:

// Yes, you can define a class inside a method.
class $AutoGeneratedName implements Comparator<Person> {
  @Override public int compare(Person p1, Person p2) {
    return p1.getAge() - p2.getAge();
  }
}

Collections.sort(persons, new $AutoGeneratedName());

In other words, short for: Define a new class, which implements Comparator. Then, instantiate this class once right away. Resolve this entire expression as a reference to this newly created instance. This construct is called an anonymous inner class.


CAREFUL - this code is bad.

20 years ago, that code was mostly fine, except for one detail: using a - b in comparisons is dangerous for very large numbers, but presumably, given that this is about 'age', not going to be an issue. Still, bad form; return Integer.comparing(p1.getAge(), p2.getAge()) would be much better.

But since then, this is no longer needed. You can write the concept much shorter like so:

Collections.sort(persons, (a, b) -> Integer.compare(a.getAge(), b.getAge());

We can do even that much, much simpler, and more readably by using List#sort with Comparator.comparingInt.

persons.sort(Comparator.comparingInt(Person::getAge));

which does exactly what you think it does when you just read it like its english: It sorts the collection 'persons' by comparing a specific int - which int? The one you get when you invoke getAge() on the person method.

This last snippet is what you should be using instead.

Related