Як перекрити рівний метод на Java


108

Я намагаюся замінити метод рівних на Java. У мене клас, Peopleякий в основному має 2 поля даних nameі age. Тепер я хочу змінити equalsметод, щоб я міг перевіряти між двома людьми об'єктами.

Мій код такий

public boolean equals(People other){
    boolean result;
    if((other == null) || (getClass() != other.getClass())){
        result = false;
    } // end if
    else{
        People otherPeople = (People)other;
        result = name.equals(other.name) &&  age.equals(other.age);
    } // end else

    return result;
} // end equals

Але коли я пишу, age.equals(other.age)це дає мені помилку, оскільки рівний метод може порівняти лише String і вік Integer.

Рішення

Я використовував ==оператор, як було запропоновано, і моя проблема вирішена.


3
Гей, як щодо цього.age == other.age? :)
denis.solonenko

1
Який тип даних для віку? int АБО Integer? Крім того, яку версію JDK ви використовуєте?
Маніш

2
"як метод рівних може порівнювати лише рядок" - Хто сказав вам, що метод рівних може порівнювати лише рядок? метод equals належить до класу Object, і будь-який створений клас матиме рівну реалізацію за замовчуванням. Ви можете назвати рівних у будь-якому класі Java
Маніш

Відповіді:


127
//Written by K@stackoverflow
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        ArrayList<Person> people = new ArrayList<Person>();
        people.add(new Person("Subash Adhikari", 28));
        people.add(new Person("K", 28));
        people.add(new Person("StackOverflow", 4));
        people.add(new Person("Subash Adhikari", 28));

        for (int i = 0; i < people.size() - 1; i++) {
            for (int y = i + 1; y <= people.size() - 1; y++) {
                boolean check = people.get(i).equals(people.get(y));

                System.out.println("-- " + people.get(i).getName() + " - VS - " + people.get(y).getName());
                System.out.println(check);
            }
        }
    }
}

//written by K@stackoverflow
    public class Person {
        private String name;
        private int age;

        public Person(String name, int age){
            this.name = name;
            this.age = age;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }

            
if (obj.getClass() != this.getClass()) {
                return false;
            }



            final Person other = (Person) obj;
            if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
                return false;
            }

            if (this.age != other.age) {
                return false;
            }

            return true;
        }

        @Override
        public int hashCode() {
            int hash = 3;
            hash = 53 * hash + (this.name != null ? this.name.hashCode() : 0);
            hash = 53 * hash + this.age;
            return hash;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

Вихід:

запустити:

- Subash Adhikari - VS - K false

- Subash Adhikari - VS - StackOverflow false

- Subash Adhikari - VS - Subash Adhikari true

- K - VS - StackOverflow false

- K - VS - Subash Adhikari false

- StackOverflow - VS - Subash Adhikari false

- БУДІВЛІ УСПІШНО (загальний час: 0 секунд)


7
для чого hash = 53 * hashти це використовуєш?
kittu

2
Використання getClass()буде викликати проблеми, якщо клас стає підкласом і порівнюється з об'єктом надкласового класу.
Tuxdude

1
може бути bcoz 53 - це просте число , подивіться на цю відповідь stackoverflow.com/a/27609/3425489 , він прокоментував, обираючи номери вhashCode()
Шантарам Тупе

1
Перемогла відповідь на це питання має чудове пояснення того , чому ви перевизначити хеш - код () stackoverflow.com/a/27609/1992108
Pegasaurus

7
Розглянемо використання if (getClass ()! = Obj.getClass ()) ... замість використання instanceofоператора або isAssignableFrom. Для цього потрібна точна відповідність типу, а не збіг підтипу. - Симетрична вимога. Також для порівняння Stringабо інших типів об’єктів ви можете використовувати Objects.equals(this.name,other.name).
YoYo

22

Введення нового підпису методу, який змінює типи параметрів, називається перевантаженням :

public boolean equals(People other){

Тут Peopleінакше, ніж Object.

Коли підпис методу залишається ідентичним підкласі його надкласу, він називається переосмисленням, а @Overrideанотація допомагає розрізнити два під час компіляції:

@Override
public boolean equals(Object other){

Не бачачи фактичної декларації age, важко сказати, чому з’являється помилка.


18

Я не впевнений у деталях, оскільки ви не опублікували весь код, але:

  • НЕ забудьте скасувати hashCode(), а
  • equalsметод повинен мати Object, а НЕ в Peopleякості типу аргументу. На даний момент ви перевантажуєте, не переосмислюючи, метод дорівнює, що, мабуть, не те, що вам потрібно, особливо якщо ви перевіряєте його тип пізніше.
  • ви можете використовувати, instanceofщоб перевірити, чи це об'єкт People, наприкладif (!(other instanceof People)) { result = false;}
  • equalsвикористовується для всіх предметів, але не примітивів. Я думаю, ви маєте на увазі вік int(примітивний), і в цьому випадку просто використовуйте ==. Зауважте, що ціле число (з великим літером "Я") є Об'єктом, який слід порівнювати з рівними.

Див. Які питання слід враховувати при переосмисленні рівних та хеш-кодів на Java? для отримання детальної інформації.


12
@Override
public boolean equals(Object that){
  if(this == that) return true;//if both of them points the same address in memory

  if(!(that instanceof People)) return false; // if "that" is not a People or a childclass

  People thatPeople = (People)that; // than we can cast it to People safely

  return this.name.equals(thatPeople.name) && this.age == thatPeople.age;// if they have the same name and same age, then the 2 objects are equal unless they're pointing to different memory adresses
}

12

Пункт 10: Дотримуйтесь загального договору, коли перевагу дорівнює

На думку Ефективної Java , переосмислення equalsметоду здається простим, але існує багато способів помилитися, і наслідки можуть бути тяжкими. Найпростіший спосіб уникнути проблем - це не перекрити equalsметод, і в цьому випадку кожен екземпляр класу дорівнює лише собі. Це правильно зробити, якщо застосовується будь-яке з наступних умов:

  • Кожен екземпляр класу за своєю суттю унікальний . Це справедливо для класів, таких як Thread, які представляють активні об'єкти, а не значення. Реалізація рівності, що надається Object, має саме правильну поведінку для цих класів.

  • Класу немає потреби в тесті «логічної рівності». Наприклад, java.util.regex.Pattern міг би змінити рівність, щоб перевірити, чи два екземпляри Шаблону представляють однаковий регулярний вираз, але дизайнери не думали, що клієнтам потрібна чи потрібна ця функціональність. За цих обставин ідеальна реалізація, успадкована від Object, є ідеальною.

  • Суперклас вже переміщений на рівні, і поведінка надкласового класу доречна для цього класу. Наприклад, більшість реалізацій Set успадковує їх рівну реалізацію від AbstractSet, Реалізації списку від AbstractList та Реалізації карт від АватарМап.

  • Клас приватний або пакет-приватний , і ви впевнені, що його метод рівності ніколи не буде використаний. Якщо ви дуже небезпечні для ризику, ви можете замінити метод рівних, щоб переконатися, що він не викликається випадково:

equalsМетод реалізує відношення еквівалентності. Він має такі властивості:

  • Рефлексивний: Для будь-якого еталонного значення ненульового x, x.equals(x)має повертати вірно.

  • Симетричний: Для будь-яких еталонних значень ненульових xі y, x.equals(y)повинні повернути істинно тоді і тільки тоді , коли y.equals (х) повертає істину.

  • Перехідний: Для будь-яких еталонних значень непустих x, y, z, якщо x.equals(y)повертається trueі y.equals(z)повертається true, то x.equals(z)повинен повернутися true.

  • Послідовно: для будь-яких ненульових опорних значень xта yдекількох викликів x.equals(y)повинно послідовно повертатись trueабо послідовно повертатись за falseумови, що жодна інформація, що використовується у порівнянні, не змінюється.

  • Для будь-якого еталонного значення ненульовим x, x.equals(null)повинен повернутися false.

Ось рецепт високоякісного рівного методу:

  1. Використовуйте ==оператор, щоб перевірити, чи аргумент є посиланням на цей об’єкт. Якщо так, поверніть істину. Це просто оптимізація продуктивності, але така, яку варто зробити, якщо порівняння є потенційно дорогим.

  2. Використовуйте instanceofоператор, щоб перевірити, чи аргумент має правильний тип. Якщо ні, поверніть помилкове. Як правило, правильний тип - це клас, в якому відбувається метод. Іноді це якийсь інтерфейс, реалізований цим класом. Використовуйте інтерфейс, якщо клас реалізує інтерфейс, який уточнює договір рівності, щоб дозволити порівняння між класами, які реалізують інтерфейс. Інтерфейси колекції, такі як Set, List, Map, Map.Entry, мають цю властивість.

  3. Передайте аргумент правильному типу. Оскільки цьому виступу передував тест-екземпляр, він гарантується успіхом.

  4. Для кожного "значущого" поля в класі перевірте, чи відповідає це поле аргументу відповідному полі цього об'єкта. Якщо всі ці тести вдаються, поверніть справжнє; в іншому випадку поверніть помилкове. Якщо тип на етапі 2 є інтерфейсом, ви повинні отримати доступ до полів аргументу методами інтерфейсу; якщо тип - клас, ви можете мати доступ до полів безпосередньо, залежно від їх доступності.

  5. Для примітивних полів, типу яких немає floatабо double, використовуйте ==оператор для порівняння; для посилальних польових об'єктів викликайте equalsметод рекурсивно; для floatполів використовувати статичний Float.compare(float, float)метод; а для doubleполів використовувати Double.compare(double, double). Спеціальна обробка поплавка і подвійних полів стало необхідним існуванням Float.NaN, -0.0fі аналогічних подвійних значень; Хоча ви могли порівнювати floatі doubleполя зі статичними методами, Float.equalsі Double.equalsце спричинило б автобоксінг при кожному порівнянні, що мало б низьку продуктивність. Для arrayполів застосуйте ці вказівки до кожного елемента. Якщо кожен елемент поля масиву є значущим, скористайтеся одним із Arrays.equalsметодів.

  6. Деякі довідкові поля об’єктів можуть законно містити null. Щоб уникнути можливості a NullPointerException, перевірте такі поля на рівність за допомогою статичного методу Objects.equals(Object, Object).

    // Class with a typical equals method
    
    public final class PhoneNumber {
    
        private final short areaCode, prefix, lineNum;
    
        public PhoneNumber(int areaCode, int prefix, int lineNum) {
    
            this.areaCode = rangeCheck(areaCode,  999, "area code");
    
            this.prefix   = rangeCheck(prefix,    999, "prefix");
    
            this.lineNum  = rangeCheck(lineNum,  9999, "line num");
    
        }
    
        private static short rangeCheck(int val, int max, String arg) {
    
            if (val < 0 || val > max)
    
               throw new IllegalArgumentException(arg + ": " + val);
    
            return (short) val;
    
        }
    
        @Override public boolean equals(Object o) {
            if (o == this)
                return true;
            if (!(o instanceof PhoneNumber))
                return false;
            PhoneNumber pn = (PhoneNumber)o;
            return pn.lineNum == lineNum && pn.prefix == prefix
                    && pn.areaCode == areaCode;
        }
        ... // Remainder omitted
    
    }

1
Не забудьте зазначити, що вам доведеться також перебороти hashCode(). Також зверніть увагу, що з моменту написання Java7 equals()і hashCode()методів стало набагато простіше за допомогою Objects.equals(), Arrays.equals()і Objects.hashCode(), Arrays.hashCode().
Арнольд Шрівер

3
Спробуйте if (getClass() != obj.getClass()) ...скоріше використовувати оператор instanceof, а не використовувати його. Для цього потрібна точна відповідність типу, а не збіг підтипу. - Симетрична вимога.
YoYo

@YoYo вірно ... використання instanceof могло б відмовити від симетричного властивості. Якщо o - це підклас PhoneNumber, як, можливо, PhoneNumberWithExtension, і він переосмислює рівне так само, використовуючи instanceof, тоді o.equals (це) не вдасться тесту instanceof, тоді як PhoneNumber.equals передасть його і поверне справжнє (припускаючи всі інші поля PhoneNumber рівні).
ldkronos

5

Оскільки я здогадуюсь ageтипу int:

public boolean equals(Object other){
    boolean result;
    if((other == null) || (getClass() != other.getClass())){
        result = false;
    } // end if
    else{
        People otherPeople = (People)other;
        result = name.equals(otherPeople.name) &&  age == otherPeople.age;
    } // end else

    return result;
} // end equals

Це призведе до NullPointerExceptionвипадку nameце null.
orien

@orien Не велика справа, можливо, саме в договорі nameніколи не отримується присвоєне nullзначення ...
fortran

@fortran Так ... можливо, це не велика справа;)
orien

5

Порівнюючи об’єкти на Java, ви робите семантичну перевірку , порівнюючи тип та ідентифікуючи стан об'єктів з:

  • себе (той самий екземпляр)
  • сам (клон або реконструйована копія)
  • інші об'єкти різного типу
  • інші об'єкти одного типу
  • null

Правила:

  • Симетрія :a.equals(b) == b.equals(a)
  • equals()завжди приносить trueабо false, але ніколи NullpointerException, ClassCastExceptionабо будь-який інший викид

Порівняння:

  • Перевірка типу : обидва екземпляри повинні бути одного типу, тобто вам потрібно порівняти фактичні класи для рівності. Це часто неправильно реалізується, коли розробники використовують instanceofдля порівняння типів (що працює лише до тих пір, поки немає підкласів, і порушує правило симетрії, коли A extends B -> a instanceof b != b instanceof a).
  • Семантична перевірка ідентифікаційного стану : переконайтеся, що ви розумієте, за яким станом ідентифікуються екземпляри. Особи можуть бути визначені за номером соціального страхування, але не за кольором волосся (можна фарбувати), назвою (можна змінити) або віком (постійно змінюється). Тільки із об'єктами значення слід порівнювати повний стан (усі неперехідні поля), інакше перевіряйте лише те, що ідентифікує екземпляр.

Для вашого Personкласу:

public boolean equals(Object obj) {

    // same instance
    if (obj == this) {
        return true;
    }
    // null
    if (obj == null) {
        return false;
    }
    // type
    if (!getClass().equals(obj.getClass())) {
        return false;
    }
    // cast and compare state
    Person other = (Person) obj;
    return Objects.equals(name, other.name) && Objects.equals(age, other.age);
}

Багаторазовий корисний клас:

public final class Equals {

    private Equals() {
        // private constructor, no instances allowed
    }

    /**
     * Convenience equals implementation, does the object equality, null and type checking, and comparison of the identifying state
     *
     * @param instance       object instance (where the equals() is implemented)
     * @param other          other instance to compare to
     * @param stateAccessors stateAccessors for state to compare, optional
     * @param <T>            instance type
     * @return true when equals, false otherwise
     */
    public static <T> boolean as(T instance, Object other, Function<? super T, Object>... stateAccessors) {
        if (instance == null) {
            return other == null;
        }
        if (instance == other) {
            return true;
        }
        if (other == null) {
            return false;
        }
        if (!instance.getClass().equals(other.getClass())) {
            return false;
        }
        if (stateAccessors == null) {
            return true;
        }
        return Stream.of(stateAccessors).allMatch(s -> Objects.equals(s.apply(instance), s.apply((T) other)));
    }
}

Для вашого Personкласу, використовуючи цей клас корисності:

public boolean equals(Object obj) {
    return Equals.as(this, obj, t -> t.name, t -> t.age);
}

1

якщо вік int, ви повинні використовувати ==, якщо це об'єкт Integer, тоді ви можете використовувати equals (). Вам також потрібно застосувати метод хеш-коду, якщо ви переосмислите рівне. Деталі контракту доступні в javadoc Object, а також на різних сторінках в Інтернеті.


0

Ось рішення, яке я нещодавно використав:

public class Test {
    public String a;
    public long b;
    public Date c;
    public String d;
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof Test)) {
            return false;
        }
        Test testOther = (Test) obj;
        return (a != null ? a.equals(testOther.a) : testOther.a == null)
                && (b == testOther.b)
                && (c != null ? c.equals(testOther.c) : testOther.c == null)
                && (d != null ? d.equals(testOther.d) : testOther.d == null);
    }

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.