Как переопределить метод equals в Java

Я пытаюсь переопределить метод равных в 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.

Решение

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

Ответ 1

//Written by [email protected]
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("Qaru", 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 [email protected]
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;
    }
}

Выход:

run:

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

- Субаш Адхикари - В.С. - Кару ложь

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

-- K - VS - Qaru false

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

- Кару - В.С. - Субаш Адхикари ложный

-- BUILD SUCCESSFUL (total time: 0 seconds)

Ответ 2

Представление новой сигнатуры метода, которая изменяет типы параметров, называется перегрузкой:

public boolean equals(People other){

Здесь People отличается от Object.

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

@Override
public boolean equals(Object other){

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

Ответ 3

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

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

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

Ответ 4

@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
}

Ответ 5

Пункт 10: Соблюдайте общий договор, если переопределение равно

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

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

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

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

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

Метод equals реализует отношение эквивалентности. У него есть эти свойства:

  • Рефлексивное: Для любого ненулевого опорного значения 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
    
    }
    

Ответ 6

Так как я предполагаю, что 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

Ответ 7

При сравнении объектов в 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);
}

Ответ 8

Если age является int, вы должны использовать ==, если это объект Integer, то вы можете использовать equals(). Вам также необходимо реализовать метод hashcode, если вы переопределите equals. Подробная информация о контракте доступна в javadoc объекта, а также на разных страницах в Интернете.

Ответ 9

Метод equals определяет параметр метода типа Object, а его возвращаемый тип является логическим.

Don’t change the name of the method, its return type, or the type of method parameter

когда вы определяете (переопределить) этот метод в своем классе для сравнения двух объектов.

public boolean equals(Object anObject) {
...
}