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

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

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

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.

Решение

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

12 ответов

Решение
//Written by K@stackru
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("Stackru", 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@stackru
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;
    }
}

Выход:

бежать:

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

- Субаш Адхикари - VS - Stackru false

- Субаш Адхикари - VS - Субаш Адхикари есть

- K - VS - Stackru false

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

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

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

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

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

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

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

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

Пункт 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.

  • Transitive: для любых ненулевых значений ссылки 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
    
    }
    
@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
}

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

Так как я угадываю 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

tl;dr

      record Person ( String name , int age ) {}  
if( 
    new Person( "Carol" , 27 )              // Compiler auto-generates implicitly the constructor.
    .equals(                                // Compiler auto-generates implicitly the `equals` method.
        new Person( "Carol" , 42 ) 
    ) 
)                                           // Returns `false`, as the name matches but the age differs.
{ … }

Подробности

Пока ваша конкретная проблема решена (используя == для проверки равенства между int примитивные значения), есть альтернатива, которая избавляет от необходимости писать этот код.

Java 16 приносит запись функции.

Запись - это краткий способ написать класс, основной целью которого является прозрачная и неизменная передача данных. Компилятор неявно создает конструктор, геттеры, & и toString.

метод предоставляется автоматически

Неявный метод по умолчанию сравнивает каждое поле члена, объявленное вами для записи. Члены могут быть объектами или примитивами, оба типа автоматически сравниваются в методе по умолчанию.

Например, если у вас есть запись с двумя полями, name & age, оба этих поля автоматически сравниваются, чтобы определить равенство между парой Person объекты.

      public record Person ( String name , int age ) {}

Попытайся.

      Person alice = new Person( "Alice" , 23 ) ;
Person alice2 = new Person( "Alice" , 23 ) ;
Person bob = new Person( "Bob" , 19 ) ;

boolean samePerson1 = alice.equals( alice2 ) ;  // true.
boolean samePerson2 = alice.equals( bob ) ;  // false.

Вы можете переопределить метод записи, если хотите, чтобы поведение было отличным от значения по умолчанию. Но если вы переопределите equals, обязательно переопределите hashCodeдля согласованной логики, как и для обычного класса Java . И подумайте дважды: всякий раз, добавляя методы к, пересматривайте, действительно ли структура записи соответствует этой проблемной области.

Совет: A record может быть определен внутри другого класса и даже локально внутри метода.

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

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

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

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

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

Вот решение, которое я недавно использовал:

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);
    }

}

Для ленивых программистов:lombokбиблиотека очень проста и экономит время. пожалуйста, взгляните на эту ссылку вместо того, чтобы писать строки кодов и правил, вам просто нужно применить эту библиотеку в вашей среде IDE, а затем просто @Data, и все готово.

      import lombok.Data;

 @Data  // this is the magic word :D
public class pojo {

int price;
String currency;
String productName; 

}

на самом деле в приведенном выше коде @Data является ярлыком для

      import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@EqualsAndHashCode
@ToString
//or instead of all above @Data 

public class pojo {

int price;
String currency;
String productName;

}
Другие вопросы по тегам