【问题标题】:Override equals method覆盖等于方法
【发布时间】:2011-06-15 20:57:23
【问题描述】:

新手问题在这里:

所以在我的大学作业中,我必须为我创建的新类重写对象类 equals 方法。

新类是“Product”,每个产品都有一个唯一的“id”属性。所以这就是我覆盖它的方式:

@Override
public boolean equals(Object obj) {
       final Product other = (Product) obj;
       if (id != other.id)
           return false;
       return true;
   }

问题是这样做是 1.5 分(满分 10 分),这让我怀疑是不是那么容易。所以我开始搜索,我发现了类似的东西:

@Override
public boolean equals(Object obj) {
       if (this == obj)
           return true;
       if (obj == null)
           return false;
       if (getClass() != obj.getClass())
           return false;
       final Product other = (Product) obj;
       if (id != other.id)
           return false;
       return true;
   }

这对我来说根本没有意义,因为我认为最后一个 if 检查所有其他 ifs 限制。你们怎么看?覆盖此方法的更好方法是什么?

谢谢!

【问题讨论】:

  • equals 的合约规定所有相等的对象必须具有相同的哈希码。因此,如果您覆盖 equals(),则必须覆盖 hashCode()。

标签: java overriding equals


【解决方案1】:

第二段代码更好:

  • 它针对x.equals(x) 进行了优化,这不是正确性所必需的,但是是一种有用的优化
  • 它处理x.equals(null)而不是抛出NullPointerException
  • 它处理完全不同类的对象,而不会像你的那样抛出 ClassCastException(例如 x.equals("foo")
  • 需要exact相同的类型来提供对称关系;否则obj.equals(x) 可能会调用不同的方法,给出不同的结果。

【讨论】:

  • 如果您继承 Product 而不更改方法 equals() 的语义,则第二段代码将不起作用。
  • @ignis:这取决于你所说的“工作”是什么意思。它不会将 Product 的实例视为等于子类的实例,不 - 但我认为这没有被破坏,它只是需要记录的行为。
  • a.equals(b) 如果 a.getClass() == Product.class 将始终返回 false,b 是 Product 的某个子类的实例,并且 a 和 b 具有相同的内部状态。因此,equals() 关系不是对称的。这违反了 java.lang.Object 的约定。
  • @ignis:只有当b.equals(a) 返回真时它才会是不对称的......如果它应该调用super.equals(a),它就不会。 如果没有这个检查,a.equals(b) 将只检查id,因此它可以返回true,而b.equals(a) 将返回false,因为a 不是Product 的同一子类的实例该子类的子类。对getClass() 的调用正是为了帮助对称。
  • @nojo:如果一个抽象类表示值的不可变组合(例如,double 的二维矩阵)但不要求特定的存储格式,则定义就这些值而言的相等性和哈希码。在这种情况下,等于 3 的 IdentityMatrix with Size` 可能与等于 DiagonalMatrixDiagonalValues 持有 {1.0,1.0,1.0} 进行比较。
【解决方案2】:

第二个版本是安全的,我会说是迂腐的。相反,您的版本可能会启动 ClassCastException,因为您假设变量 obj 的运行时类型是产品类型。这不是真的,这就是你应该使用this.getClass() != obj.getClass() 的原因(你也可以使用instanceof 运算符解决这个问题)。

如果我这样做

Product p = new Product();
p.equals("abc");

我得到了一个例外,而我应该得到false

此外,它还管理product.equals(null) 问题,该问题应返回false,如文档中equals 合同方法中所述。如果你不关心这个并且你关心,那么在你里面等于:

...
Product p = (Product)obj; // obj is null
obj.id // this throws a NullPointerException

【讨论】:

  • 在非最终类中使用 instanceof 如果该类是子类,则会出现对称问题。
【解决方案3】:

用于覆盖equals()的常用习语是

@Override
public boolean equals(Object obj) {
       if (! (obj instanceof Product) ) return false;

       final Product other = (Product) obj;
       if (id != other.id)
           return false;
       return true;
   }

在您发布的第二个版本中:

  • 第一个 if() 可能对 仅当满足以下条件时才进行优化 支票太贵了。但 事实并非如此,所以这只是 多余的代码是邪恶的。
  • 如果您定义一个 Product 子类,该版本将不起作用 不会改变方法 equals() 的语义。 (例如一个类 提供了一些方便的方法,但是 没有额外的内部状态 对象。)这是因为 第三个 if()。

【讨论】:

    【解决方案4】:

    第二个是Effective Java中最安全的覆盖equals的方法。 1 如果 Object 为 null 并且它没有尽可能优化(不检查 ojb 是否是对自身的引用),则 1 有一个空指针

    【讨论】:

      【解决方案5】:

      'Joshua Bloch: Effective Java' 建议的解决方案是(假设Product 没有Object 以外的超类):

      @Override
      public boolean equals(Object obj) {
          if (this == obj) return true;
          if (!(obj instanceof Product)) return false;
          final Product other = (Product) obj;
          if (id != other.id) return false;
          return true;
      }
      

      您的第一个解决方案有两个缺点:

      • new Product(1).equals(null) 抛出 NullpointerException,尽管它在 Object.equals() 中指定返回 false。
      • new Product(1).equals(new Vector()) 抛出 ClassCastException,尽管它在 Object.equals() 中指定返回 false。

      这两种情况都可以通过实例检查来解决。 if (this == obj) return true; 通常对提高效率很有用,但在这里可能不是必需的。

      您发布的第二个解决方案使编写具有良好语义的 Product 子类变得困难。如果你有一个子类

      public class SubProduct extends Product {
          SubProduct(int id) {
               super(id);
          }
          ...
      }
      

      您将拥有!new Product(4).equals(new SubProduct(4))。这违反了 Liskov 的替代原则,并且通常被认为不是那么好。如果你有最后一堂课 第二种方案同上。

      【讨论】:

      • 考虑如果SubProduct 有更多状态并进一步覆盖equals - 那么如果你使用instanceof 你最终会得到a.equals(b) != b.equals(a),这违反了Object 合约。就 Liskov 而言,使用确切的类可能并不总是您想要的,但它确实允许在不违反 Object 合同的情况下进行子类化。继承层次结构的平等基本上是痛苦的:(
      • 完全同意。我小心翼翼地避免说“错”。但是,我没有仔细阅读其他答案,并且错过了您更简洁的反例。
      猜你喜欢
      • 2013-02-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-17
      • 2015-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多