Skip to content

Why compareTo throws NPE using a comparator who doesn’t do that when user separatedly

I created a comparator that never throws NPE. However, when using it inside compareTo, it throws NPE. Why?

public class Person implements Comparable<Person> {
    public static final Comparator<Person> BIRTHDATE_ASCENDING_NULLS_FIRST = Comparator
            .nullsFirst(Comparator.comparing(Person::getBirthDate, Comparator.nullsFirst(Comparator.naturalOrder())));

    private String name;
    private LocalDate birthDate;

    public Person() {

    public Person(String name, LocalDate birthDate) {
        this(); = name;
        this.birthDate = birthDate;

    public String getName() {
        return name;

    public void setName(String name) { = name;

    public LocalDate getBirthDate() {
        return birthDate;

    public void setBirthDate(LocalDate birthDate) {
        this.birthDate = birthDate;

    public String toString() {
        return name + " was born on " + (birthDate == null ? "???" : birthDate);

    public int compareTo(Person other) {
        if (other == null) {
            return 1;
        } else if (getBirthDate() == null ^ other.getBirthDate() == null) {
            // nulls first
            return getBirthDate() == null ? -1 : 1;
        } else if (getBirthDate() == null) {
            // both are null
            return 0;
        System.out.println(this.toString() + ", " + other.toString());
        return, other);

    public int hashCode() {
        int result = 1;

        result = 31 * result + (birthDate == null ? System.identityHashCode(this) : birthDate.hashCode());

        return result;

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;

        if (obj == null || getClass() != obj.getClass()) {
            return false;

        return Objects.equals(birthDate, ((Person) obj).getBirthDate());

    public static void main(String[] args) {
        List<Person> people = new ArrayList<>();
        people.add(new Person("John", null));
        people.add(new Person("Mary",;
        people.add(new Person("George",;

        Collections.sort(people, BIRTHDATE_ASCENDING_NULLS_FIRST);


        Collections.sort(people, BIRTHDATE_ASCENDING_NULLS_FIRST.reversed());

        // This one throws NPE

When expliciting the comparator on Collections.sort call, sort operation doesn’t use compareTo implementation, as expected.
When not doing this, sort operation uses the implementation of compareTo. Since this method calls the exact same comparator, why do I get NPE here? I mean, why the comparator doesn’t handle NPE when being called from compareTo?


The other calls to sort don’t throw NPE because they call The call probably looks something like this:, item2)

An actual example is located at line 355:

if ([runHi++], a[lo]) < 0) {

However, the sort overload that doesn’t take a Comparator (the one that throws a NPE), calls Person.compareTo. The call looks something like:


An actual example is located at line 321:

while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) < 0)

Now, no matter what null handling the inner logic of compareTo does, the above will throw a NPE if item1 is null. Your implementation of compareTo only prevents an NPE if item2, the parameter is null. It can handle a null parameter, but it can’t change how is called. If it is called on a null object, an NPE is still thrown.