Как переопределить метод equals в Java - PullRequest
95 голосов
/ 18 ноября 2011

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

Мой код выглядит следующим образом

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), это дает мне ошибку, поскольку метод equals может сравнивать только String, а age - Integer.

Решение

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

Ответы [ 8 ]

113 голосов
/ 18 ноября 2011
//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 (!Person.class.isAssignableFrom(obj.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 - StackOverflow false

- Субаш Адхикари - VS - Субаш Адхикари правда

- K - VS - StackOverflow false

- K - VS - Субаш Адхикари ложь

- Переполнение стека - VS - Subash Adhikari false

- СТРОИТЬ УСПЕШНО (общее время: 0 секунд)

20 голосов
/ 18 ноября 2011

Прежде всего: вы не переопределяете equals, вы перегружаете это.

Не видя фактического объявления age, трудно сказать, почему вы получаете ошибку.

17 голосов
/ 18 ноября 2011

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

  • также не забудьте переопределить hashCode()
  • метод equals должен иметь Object, а не People в качестве типа аргумента. В данный момент вы перегружаете, а не переопределяете метод equals, что, вероятно, не то, что вам нужно, особенно если учесть, что вы проверяете его тип позже.
  • Вы можете использовать instanceof, чтобы проверить, что это объект People, например if (!(other instanceof People)) { result = false;}
  • equals используется для всех объектов, но не для примитивов. Я думаю, что вы имеете в виду возраст int (примитив), в этом случае просто используйте ==. Обратите внимание, что целое число (с большой буквы «I») - это объект, который следует сравнивать с равными.

См. Какие проблемы следует учитывать при переопределении equals и hashCode в Java? для получения более подробной информации.

10 голосов
/ 27 ноября 2015
@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
}
9 голосов
/ 02 апреля 2018

Пункт 10: подчиняться генеральному контракту, если переопределение равно

Согласно Effective Java , переопределение метода equals кажется простым, но есть много способов ошибиться, и последствия могут быть ужасными. Самый простой способ избежать проблем - не переопределять метод equals, и в этом случае каждый экземпляр класса равен только самому себе. Это правильно, если применимо любое из следующих условий:

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

  • Нет необходимости в том, чтобы класс предоставлял тест на «логическое равенство». Например, java.util.regex.Pattern мог иметь переопределенные равенства для проверки, точно ли представлены два экземпляра Pattern. то же самое регулярное выражение, но дизайнеры не думали, что клиенты будут нуждаться или хотеть эту функциональность. При этих обстоятельствах реализация equals, унаследованная от Object, является идеальной.

  • Суперкласс уже переопределен равными, , и поведение суперкласса подходит для этого класса. Например, большинство реализаций Set наследуют свою реализацию equals из AbstractSet, реализации List из AbstractList и реализации Map из AbstractMap.

  • Класс является приватным или закрытым для пакета , и вы уверены, что его метод equals никогда не будет вызываться. Если вы крайне склонны к риску, вы можете переопределить метод equals, чтобы гарантировать, что он не будет вызван случайно:

Метод equals реализует отношение эквивалентности. Он имеет следующие свойства:

  • Reflexive:. Для любого ненулевого опорного значения x x.equals(x) должен вернуть истинный

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

  • Транзитивно: для любых ненулевых опорных значений 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, чтобы проверить, имеет ли аргумент правильный тип. Если нет, верните false. Как правило, правильный тип - это класс, в котором встречается метод. Иногда это какой-то интерфейс, реализованный этим классом. Используйте интерфейс, если класс реализует интерфейс, который уточняет контракт равенства, чтобы разрешить сравнения между классами, которые реализуют интерфейс. Интерфейсы коллекции, такие как Set, List, Map и Map.Entry, имеют это свойство.

  3. Приведите аргумент к правильному типу. Поскольку этому приведению предшествовал экземпляр теста, он гарантированно будет успешным.

  4. Для каждого «значимого» поля в классе проверьте, соответствует ли это поле аргумента соответствующему полю этого объекта. Если все эти тесты пройдены успешно, верните true; в противном случае верните false. Если тип в Шаге 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. Чтобы избежать возможности 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
    
    }
    
5 голосов
/ 18 ноября 2011

Так как я предполагаю, 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
4 голосов
/ 21 марта 2018

При сравнении объектов в 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 голос
/ 18 ноября 2011

если age - int, вы должны использовать ==, если это объект Integer, тогда вы можете использовать equals ().Вам также нужно реализовать метод хеш-кода, если вы переопределяете equals.Детали контракта доступны в javadoc Object, а также на различных страницах в сети.

...