有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

基类和子类中的Java equals方法

我有一个简单的基类,后来被许多单独的类扩展,这些类可能引入新的字段,但不一定。我在基类中定义了一个equals方法,但也为一些子类重写了它。在基类/子类中混合定义可以吗?在我的例子中,这是为了避免代码重复检查相同的字段


共 (6) 个答案

  1. # 1 楼答案

    我想,在super{}中提供equals(Object obj)hashCode()方法实现就像Java所做的那样是完美的。我们都知道Java在基类java.lang.Object中提供了hashCode() and equals(Object obj)方法实现,当需要时,我们在我们的class中提供了override方法实现

  2. # 2 楼答案

    这是一个相当有效的方法。该问题存在于您的一个子类中,它必须保留由其父类约束的相等定义。否则,您将有一个中断的equals函数,这可能会导致在运行时出现一些非常独特的情况

  3. # 3 楼答案

    虽然下面的内容并不适用于所有情况,但我发现它非常实用。当我同时拥有一个超类和一个子类时,我已经使用过很多次了。我不想相互比较它们,但我也不想为子类重新实现所有的超类equals()。它处理:

    • a、 等于(b)==b等于(a)
    • 不复制字段比较代码
    • 容易推广到任何子类深度
    • 子类。等于(超类)==false
    • 超类。等于(子类)==false

    代码示例

    // implement both strict and asymmetric equality
    class SuperClass {
       public int f1;
       public boolean looseEquals(Object o) {
          if (!(o instanceof SuperClass)) return false;
          SuperClass other = (SuperClass)o;
          return f1 == other.f1;
       }
       @Override public boolean equals(Object o) {
          return looseEquals(o) && this.getClass() == o.getClass();
       }
    }
    class SubClass extends SuperClass {
       public int f2;
       @Override public boolean looseEquals(Object o) {
          if (!super.looseEquals(o)) return false;
          if (!(o instanceof SubClass)) return false;
          SubClass other = (SubClass)o;
          return f2 == other.f2;
       }
       // no need to override equals()
    }
    
  4. # 4 楼答案

    看看安吉丽卡·兰格的"Implementing equals() To Allow Mixed-Type Comparison"

    以下是一些问题的简要说明和可能的解决方案:

    平等合同(除其他外)规定:

    It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.

    这意味着您可能会遇到问题,如果您的子类正在引入新字段,并且您正在将基类的对象(或另一个不重写等于的子类)与该子类的对象进行比较

    不要执行以下操作:

    class BaseClass {
        private int field1 = 0;
    
        @Override
        public boolean equals(Object obj) {
            if (obj instanceof BaseClass) {
                return field1 == ((BaseClass) obj).field1;
            }
            return false;
        }
    }
    
    class BadSubClass extends BaseClass {
        private int field2 = 0;
    
        @Override
        public boolean equals(Object obj) {
            if (obj instanceof BadSubClass) {
                return super.equals(obj) 
                        && field2 == ((BadSubClass) obj).field2;
            }
            return false;
        }
    }
    

    因为你得到了

    BaseClass baseClass = new BaseClass();
    BadSubClass subClass = new BadSubClass();
    
    System.out.println(baseClass.equals(subClass)); // prints 'true'
    System.out.println(subClass.equals(baseClass)); // prints 'false'
    

    可能的解决方案:

    用类比较替换instanceof-检查:

    obj != null && obj.getClass() == getClass()
    

    使用此解决方案,BaseClass的对象永远不会等于任何子类的对象

    如果创建另一个SubClass而不使用equals方法的@Override,两个SubClass-对象可以在开箱即用的情况下彼此相等(如果BaseClass.equals复选框决定是这样),但是SubClass-对象永远不会等于BaseClass-对象

    一个好的实施可以如下所示:

    class BaseClass {
        private int field1 = 0;
    
        @Override
        public boolean equals(Object obj) {
            if (obj != null && obj.getClass() == getClass()) {
                return field1 == ((BaseClass) obj).field1;
            }
            return false;
        }
    }
    
    class GoodSubClass extends BaseClass {
        private int field2 = 0;
    
        @Override
        public boolean equals(Object obj) {
            if (obj instanceof GoodSubClass) {
                return super.equals(obj) && field2 == ((GoodSubClass) obj).field2;
            }
            return false;
        }
    }
    

    请参阅上面提到的文章,了解更高级的问题及其解决方案

  5. # 5 楼答案

    您可以使用super()方法来调用您正在扩展的类的方法,以防止任何代码重复的需要

    public class BaseClass {
      public boolean equals(BaseClass other) {
        return (other.getBlahblah() == this.Blahblah && .....);
      }
    }
    
    public class DerivedClass extends BaseClass {
      public boolean equals(DerivedClass other) {
        return (super(other) && other.getNewAttribute() == this.NewAttribute.....);
      }
    }
    
  6. # 6 楼答案

    不,在引入与equals方法相关的新字段时,不可能遵守equals契约。有关更多信息,请参阅Joshua Bloch的“高效Java”

    编辑:

    我现在手头没有这本书,但是我认为如果基类是抽象的/不能实例化的,那也没关系