Метод сравнения нарушает свой общий контракт и метод compareTo

У меня есть класс Contact с полями firstName, lastName и emails. Мне нужно отсортировать их с помощью Collection.sort(...), но у меня есть исключение:

java.lang.IllegalArgumentException: метод сравнения нарушает свой общий контракт!

Мой метод compareTo:

    @Override
public int compareTo(Contact another) {
    int compareFirstName = 0;
    if (this.getFirstName() != null && another.getFirstName() != null) {
        compareFirstName = this.getFirstName().compareToIgnoreCase(
                another.getFirstName());

        if (compareFirstName == 0) {
            int compareLastName = 0;
            if (this.getLastName() != null && another.getLastName() != null) {
                compareLastName = this.getLastName().compareToIgnoreCase(
                        another.getLastName());

                if (compareLastName == 0) {
                    int compareEmail = 0;
                    if (this.getEmails() != null
                            && another.getEmails() != null) {
                        compareEmail = this.getEmails()
                                .compareToIgnoreCase(another.getEmails());

                        return compareEmail;
                    } else {

                        return 0;
                    }
                } else {
                    return compareLastName;
                }
            } else {
                int compareEmail = 0;
                if (this.getEmails() != null && another.getEmails() != null) {
                    compareEmail = this.getEmails().compareToIgnoreCase(
                            another.getEmails());

                    return compareEmail;
                } else {

                    return 0;
                }
            }
        } else {

            return compareFirstName;
        }
    } else {
        int compareLastName = 0;
        if (this.getLastName() != null && another.getLastName() != null) {
            compareLastName = this.getLastName().compareToIgnoreCase(
                    another.getLastName());

            if (compareLastName == 0) {
                int compareEmail = 0;
                if (this.getEmails() != null && another.getEmails() != null) {
                    compareEmail = this.getEmails().compareToIgnoreCase(
                            another.getEmails());

                    return compareEmail;
                } else {

                    return 0;
                }
            } else {

                return compareLastName;
            }
        } else {
            int compareEmail = 0;
            if (this.getEmails() != null && another.getEmails() != null) {
                compareEmail = this.getEmails().compareToIgnoreCase(
                        another.getEmails());

                return compareEmail;
            } else {

                return 0;
            }
        }
    }
}

Пожалуйста, помогите мне найти ошибку в моем методе compareTo. Спасибо.


person dan41k    schedule 13.10.2014    source источник
comment
Используете ли вы дженерики в декларациях реализации? Сопоставимые‹Контакты›?   -  person MadProgrammer    schedule 13.10.2014
comment
да. открытый класс Contact реализует FactoryObject, Comparable‹Contact›   -  person dan41k    schedule 13.10.2014


Ответы (1)


Ваша реализация нарушает контракт.

Предположим, у вас есть 3 контакта:

contact1 : First Name = "John", Last Name = "Doe", Email = "[email protected]"
contact2 : First Name = "John", Last Name = "Doe", Email = null
contact3 : First Name = "John", Last Name = null, Email = "[email protected]"

Исходя из вашей логики:

contact1.compareTo(contact2) возвращает 0 (поскольку у них одинаковые имя и фамилия).
contact2.compareTo(contact3) также возвращает 0 (поскольку вы сравниваете только по имени).
Но contact1.compareTo( contact3) не возвращает 0 (поскольку у них разные адреса электронной почты).

compareTo должен быть транзитивным.

Способ исправить это — не игнорировать свойство, которое имеет значение null только в одном из сравниваемых контактов. Например, если this.getLastName()==null && other.getLastName() != null, верните 1 (при условии, что вы хотите упорядочить null фамилий после ненулевых фамилий).

person Eran    schedule 13.10.2014
comment
@dan41k Он не обязательно будет больше, чем сейчас, поскольку, например, если this.getFirstName()==null && other.getFirstName()!=null, вы можете немедленно вернуться, не проверяя фамилию и адрес электронной почты. Вы сравниваете фамилии только в том случае, если оба имени равны нулю. - person Eran; 13.10.2014