如何在Java中重写equals方法

时间:2011-11-18 09:41:39

标签: java override equals

我试图在Java中覆盖equals方法。我有一个班级People,它基本上有2个数据字段nameage。现在我想覆盖equals方法,以便我可以检查2个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。

解决方案

我建议使用==运算符,我的问题就解决了。

10 个答案:

答案 0 :(得分:111)

//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 - 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秒)

答案 1 :(得分:20)

首先:你没有覆盖 equals,你重载

如果没有看到age的实际声明,很难说你为什么会收到错误。

答案 2 :(得分:17)

我不确定细节,因为你没有发布整个代码,但是:

  • 记得也要覆盖hashCode()
  • equals方法应该有Object,而不是People作为参数类型。目前你正在超载,而不是覆盖equals方法,这可能不是你想要的,特别是考虑到你以后检查它的类型。
  • 您可以使用instanceof检查它是否为人物对象,例如if (!(other instanceof People)) { result = false;}
  • equals用于所有对象,但不用于基元。我认为你的意思是年龄是int(原始),在这种情况下只需使用==。请注意,Integer(大写'I')是一个Object,应与equals进行比较。

有关详细信息,请参阅What issues should be considered when overriding equals and hashCode in Java?

答案 3 :(得分:10)

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

答案 4 :(得分:8)

第10项:在覆盖等于

时遵守一般合同
  

According to Effective Java,覆盖equals方法似乎很简单,但有很多方法可以解决问题,后果可能很严重。避免问题的最简单方法是不覆盖equals方法,在这种情况下,类的每个实例仅与自身相等。如果符合以下任何条件,这是正确的做法:

  • 该类的每个实例本质上都是唯一的。对于表示活动实体而不是值的Thread这样的类也是如此。 Object提供的equals实现对这些类具有完全正确的行为。

  • 该类不需要提供“逻辑相等”测试。例如,java.util.regex.Pattern可以重写等于检查两个Pattern实例是否代表完全相同的正则表达式,但设计师并不认为客户端需要或想要这个功能。在这些情况下,从Object继承的equals实现是理想的。

  • 超类已经覆盖了equals,,并且超类行为适用于此类。例如,大多数Set实现从AbstractSet继承它们的equals实现,从AbstractList继承List实现,从AbstractMap继承Map实现。

  • 该类是私有的或包私有的,并且您确定永远不会调用其equals方法。如果您非常冒险,可以覆盖equals方法以确保不会意外调用它:

equals方法实现等价关系。它具有以下属性:

  • 自反:对于任何非空引用值xx.equals(x)必须返回true。

  • 对称:对于任何非空引用值xy,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。

  • 传递:对于任何非空参考值xyz,如果x.equals(y)返回true和{{1} }返回y.equals(z),然后true必须返回x.equals(z)

  • 一致:对于任何非空引用值truexy的多次调用必须始终返回x.equals(y)或始终返回true如果修改了等比较中使用的信息,则不提。

  • 对于任何非空引用值falsex必须返回x.equals(null)

这是一个高质量的等于方法的配方:

  1. 使用false运算符检查参数是否是对此对象的引用。如果是这样,返回true。这只是一个性能优化,但如果比较可能很昂贵,那么值得做。

  2. 使用==运算符检查参数的类型是否正确。如果没有,则返回false。通常,正确的类型是方法发生的类。偶尔,它是由这个类实现的一些接口。如果类实现了一个优化equals协定的接口,则允许使用接口,以允许跨实现接口的类进行比较。集合接口(如Set,List,Map和Map.Entry)具有此属性。

  3. 将参数转换为正确的类型。因为此强制转换前面有一个instanceof测试,所以保证成功。

  4. 对于类中的每个“重要”字段,检查参数的该字段是否与此对象的相应字段匹配。如果所有这些测试都成功,则返回true;否则,返回false。如果步骤2中的类型是接口,则必须通过接口方法访问参数的字段;如果类型是一个类,您可以直接访问这些字段,具体取决于它们的可访问性。

  5. 对于类型不是instanceoffloat的原始字段,请使用double运算符进行比较;对于对象引用字段,递归调用==方法;对于equals字段,请使用静态float方法;对于Float.compare(float, float)字段,请使用doubleDouble.compare(double, double)Float.NaN和类似的双重值的存在使得浮点和双场的特殊处理成为必要;虽然您可以将-0.0ffloat字段与静态方法doubleFloat.equals进行比较,但这会在每次比较时产生自动装箱,这会导致性能下降。对于Double.equals字段,请将这些指南应用于每个元素。如果数组字段中的每个元素都很重要,请使用array方法之一。

  6. 某些对象引用字段可能合法地包含Arrays.equals。为避免null的可能性,请使用静态方法NullPointerException检查此类字段是否相等。

    Objects.equals(Object, Object)

答案 5 :(得分: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

答案 6 :(得分:4)

在比较Java中的对象时,进行语义检查,将对象的类型和识别状态比较为:

  • 本身(同一个实例)
  • 本身(克隆或重建的副本)
  • 其他不同类型的对象
  • 相同类型的其他对象
  • null

规则:

  • 对称a.equals(b) == b.equals(a)
  • equals()始终会产生truefalse,但永远不会产生NullpointerExceptionClassCastException或任何其他可抛出的

比较:

  • 类型检查:两个实例都必须属于相同类型,这意味着您必须比较实际的相等类。当开发人员使用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);
}

答案 7 :(得分:1)

如果age是int,你应该使用==如果它是Integer对象,那么你可以使用equals()。 如果覆盖equals,还需要实现hashcode方法。合同的细节可以在Object的javadoc以及web中的各个页面中找到。

答案 8 :(得分:0)

这是我最近使用的解决方案:

(
    (
        (subject in ("facebook", "glassdoor")) 
        AND (date > "2020-01-01" AND date < "2020-05-31")
    ) 
    OR (from == "mike") 
    OR ((attachment == "pdf") OR ((attachment == "xls*"))
)

答案 9 :(得分:0)

tl;博士

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 原始值之间的相等性测试),但还有一种替代方法可以消除编写该代码的需要。

record

Java 16 带来了 record 功能。

记录是编写类的一种简短方式,其主要目的是透明且不可变地携带数据。编译器隐式创建构造函数、getter、equals & hashCodetoString

equals 方法自动提供

默认的隐式 equals 方法比较您为记录声明的每个成员字段。成员可以是对象或基元,默认 equals 方法中会自动比较这两种类型。

例如,如果您有一个 Person 记录携带两个字段,nameage,这两个字段会自动比较以确定一对 {{1} } 对象。

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,请确保覆盖 equals 以获得一致的逻辑,如 you would for a conventional Java class。而且,三思而后行:每当向 hashCode 添加方法时,重新考虑记录结构是否真的适合该问题域。

提示:record 可以在另一个类中定义,甚至可以在方法中本地定义。