【问题标题】:Java - equals method in base class and in subclassesJava - 基类和子类中的等于方法
【发布时间】:2012-10-21 03:52:47
【问题描述】:

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

【问题讨论】:

  • 你能解释一下混合定义是什么意思吗?谢谢。
  • 在基类中有一个定义,可能/可能不会被覆盖。我的意思是说“基类定义与子类定义的混合方法”

标签: java equals


【解决方案1】:

我认为只要遵守eqauls()hashcode() 合同就可以了。

【讨论】:

    【解决方案2】:

    您可以使用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.....);
      }
    }
    

    【讨论】:

    • +1 建议先致电 super。请注意 - 它不是有效的 Java 代码,纠正它会很酷(我理解它可能只是一个伪代码作为示例)。
    • 谢谢。我只是把它捣碎了,没有对大多数东西做检查。自从我做 Java 以来已经有一段时间了,一定已经在其中融入了一些其他的语法和调用。
    • 你不是覆盖对象的等于,而是重载它
    【解决方案3】:

    相当有效的方法。问题在于您的其中一个子类必须保留受其父级约束的 equals 定义。否则你有一个损坏的 equals 函数,这可能会在运行时导致一些非常独特的场景。

    【讨论】:

      【解决方案4】:

      不,在引入与 equals 方法相关的新字段时,不可能符合 equals 约定。有关详细信息,请参阅 Joshua Bloch 的“Effective Java”。

      编辑:

      我现在手头没有这本书,但我认为如果基类是抽象的/无法实例化就可以了。

      【讨论】:

      • +1 因为经常提到布洛赫。因为那是真的
      • 继承是不可能的,但组合是可能的(在 Effective Java 的同一项中说明)
      【解决方案5】:

      我想,在super class 中提供equals(Object obj)hashCode() 方法实现就像Java 一样完美。我们都知道,Java 在基类java.lang.Object 中提供了hashCode() and equals(Object obj) 方法实现,并且当需要时,我们在class 中提供override 它们。

      【讨论】:

        【解决方案6】:

        看看来自 Angelika Langer 的"Implementing equals() To Allow Mixed-Type Comparison"

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

        平等合同说(其中包括):

        它是对称的:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应该返回 true。

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

        请勿执行以下操作:

        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-check 替换为类比较:

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

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

        如果您在没有 equals 方法的 @Override 的情况下创建另一个 SubClass,则两个 SubClass-objects 可以彼此相等(如果 BaseClass.equals 检查决定如此)开箱即用,但是SubClass-object 永远不会等于 BaseClass-object。

        一个好的实现可能如下:

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

        有关更高级的问题及其解决方案,请参阅上述文章。

        【讨论】:

        • 感谢 Angelika Langer 文章链接。虽然这违反了 Liskov 替换原则,AFAIK。再次参见 Joshua Block 的“Effective Java”。 (不过,我还没有阅读完整的文章。)
        • o.getClass() 有什么问题!= this.getClass()?
        • @Mukul 我认为这实际上更好,因为如果你不做额外的检查,你就不需要重写 equals 方法,而且你可以在不破坏一切的情况下调用super.equals()。我刚刚提交了相应的编辑
        • GoodSubClassequals()中,我们需要检查obj instanceof GoodSubClass吗? super.equals(obj) 不是已经报道了吗?
        【解决方案7】:

        虽然以下内容不能处理所有情况,但我发现它非常实用。当我同时使用 SuperClass 和 SubClass 时,我已经使用过很多次了。我不想相互比较它们,但我也不想为 SubClass 重新实现所有 SuperClass equals()。它处理:

        • a.equals(b) == b.equals(a)
        • 不重复字段比较代码
        • 很容易推广到任何子类深度
        • Subclass.equals(SuperClass) == false
        • Superclass.equals(SubClass) == 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()
        }
        

        【讨论】:

          【解决方案8】:

          如果您没有正确编写代码,则会产生一个严重的问题,称为 不对称(违反平等契约)所以让我们看看我们的选择。

          最佳实践 – 同课策略。如果 B 是 A 的子类 每个类都有自己的equals方法,实现 使用相同的类策略,那么 B 类应该是 宣布最终以防止引入不对称 在 B 的任何未来子类中定义 equals。

          问题。如果我们不想让 B 成为最终版本怎么办?

          使用组合而不是继承。每当 B 类和 A,其中 B 是 A 的子类,需要不同的 equals 方法, 使用组合而不是继承是一个很好的策略,并且 如果无法选择最终 B 类,这是唯一安全的方法 处理等于。

          怎么样?

           public class A{
          public boolean equals(Object ob){
          //write your code here
          }
          }
          
          class B{
          A a= new A();
          public B(A a){
          this.a= a;
          }
          public boolean equals(Object ob){
          //...write your code here
          if(!((B)ob).a).equals(a)) return false;
          //...write your code here
          
          }
          }
          

          【讨论】:

            猜你喜欢
            • 2013-11-11
            • 2022-01-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-10-08
            • 1970-01-01
            • 2015-11-01
            相关资源
            最近更新 更多