如何在Java中重写equals方法

100 java overriding 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
Run Code Online (Sandbox Code Playgroud)

但是当我写age.equals(other.age)它给我错误时,equals方法只能比较String和age是Integer.

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

小智 119

//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;
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

跑:

- Subash Adhikari - VS - K false

- Subash Adhikari - VS - StackOverflow false

- Subash Adhikari - VS - Subash Adhikari true

- K - VS - StackOverflow错误

- K - VS - Subash Adhikari假

- StackOverflow - VS - Subash Adhikari false

- 建立成功(总时间:0秒)

  • 什么是`hash = 53*hash`你为什么要使用它? (6认同)
  • 考虑使用if(getClass()!= obj.getClass())...而不是使用`instanceof`operator或`isAssignableFrom`.这将需要精确的类型匹配,而不是子类型匹配. - 对称要求.另外,要比较`String`或其他Object类型,可以使用`Objects.equals(this.name,other.name)`. (5认同)
  • 没有解释为什么hashcode也被覆盖. (4认同)
  • 如果类被子类化,则使用`getClass()`将导致问题,并与超类的对象进行比较. (2认同)
  • 这个问题的获胜答案很好地解释了为什么要覆盖 hashCode() /sf/answers/1932661/ (2认同)

for*_*ran 21

首先:你没有压倒性的 People,你正在超载它.

没有看到实际的声明Object,很难说你为什么会得到错误.


Adr*_*uat 17

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

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

请参阅在Java中覆盖equals和hashCode时应考虑哪些问题?更多细节.


Nev*_*rJr 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
}
Run Code Online (Sandbox Code Playgroud)


小智 10

第10项:在超越平等时遵守总合同

根据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方法实现了等价关系.它具有以下属性:

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

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

  • 传递性:对于任何非空的参考值x,y,z,如果x.equals(y)回报率truey.equals(z)回报率true,那么x.equals(z)必须返回true.

  • 一致:对于任何非空引用值,x并且y多次调用x.equals(y)必须始终返回true或始终返回false,前提是不会修改equals比较中使用的信息.

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

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

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

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

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

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

  5. 对于类型不是float或的原始字段double,使用==运算符进行比较; 对于对象引用字段,equals递归调用该方法; 对于float字段,使用静态Float.compare(float, float)方法; 对于double领域,使用Double.compare(double, double).浮点和双精度字段的特殊处理是由存在的作了必要的Float.NaN,-0.0f并且类似的双值; 虽然你可以比较floatdouble领域与静态方法Float.equalsDouble.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
    
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • 考虑使用 `if (getClass() != obj.getClass()) ...` 而不是使用 instanceof 运算符。这将需要 *exact* 类型匹配,而不是子类型匹配。- 对称要求。 (4认同)
  • 不要忘记提及您还必须覆盖 `hashCode()`。另请注意,自 Java7 以来,通过使用“Objects.equals()”、“Arrays.equals()”和“Objects.hashCode()”,编写“equals()”和“hashCode()”方法变得更加容易,“ Arrays.hashCode()`。 (2认同)

Luc*_*ore 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
Run Code Online (Sandbox Code Playgroud)


Pet*_*ser 5

在 Java 中比较对象时,您会进行语义检查,将对象的类型和识别状态与以下各项进行比较:

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

规则:

  • 对称性a.equals(b) == b.equals(a)
  • equals()总是产生trueor false,但从来没有 a 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);
}
Run Code Online (Sandbox Code Playgroud)

可重用的通用实用程序类:

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)));
    }
}
Run Code Online (Sandbox Code Playgroud)

对于您的Person课程,使用此实用程序类:

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