【问题标题】:Inheritance - using super.equals() in subclasses that override methods used in equals of superclass继承 - 在子类中使用 super.equals() 覆盖超类中使用的方法
【发布时间】:2013-11-25 23:01:14
【问题描述】:

我一直在测试一段代码,偶然发现一个问题:你是否应该在子类中调用super.equals() 方法来覆盖超类的equals() 方法中使用的一些方法?

让我们考虑以下代码:

public abstract class Item {
    private int id;
    private float price;

    public Item(int id, String name, float price, String category) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.category = category;
    }

    public int getID() {
        return id;
    }

    public float getPrice() {
        return price;
    }

    @Override
    public boolean equals(Object object){
        if(object instanceof Item){
            Item item = (Item) object;
            if( id == item.getID()
                && price == item.getPrice())                    
            { return true; }
        }
        return false;
    }
}

还有子类 DiscountedItem:

public class DiscountedItem extends Item {
    // discount stored in %
    private int discount;

    @Override
    public boolean equals(Object object) {
        if(object instanceof DiscountedItem){
            DiscountedItem item = (DiscountedItem) object;
            return (super.equals(item)
                    && discount == item.getDiscount()
            );
        }
        return false;
    }

    public int getDiscount() {
        return discount;
    }

    @Override
    public float getPrice() {
        return super.getPrice()*(100 - discount);
    }    
}

我一直在重读Angelika Langer's secrets of equals(),她甚至说:

如果该类具有 Object 以外的超类,则应该调用 super.equals()。

但我认为子类何时会覆盖某些方法是非常不可预测的。例如,当我使用 equals 比较 2 个 DiscountedItem 对象时,调用 super 方法并将 item.getPrice() 动态分派到子类 DiscountedItem 中的正确方法,而其他价格值则使用变量直接访问。

那么,这真的取决于我(因为我应该正确实施该方法)还是有办法解决它?

【问题讨论】:

  • 您的子类的equals 方法应该以与该方法的约定一致的方式实现。委托给super.equals() 的原因是子类可能无法访问super.equals() 可能依赖的父类的字段。如果你违反封装,所有的赌注都会被取消。如果您通过使子类违反其父类的合同而未能很好地实现super.equals(),那么再次,所有的赌注都没有了。
  • 有人可能会说,这里的根本问题是在 bean getter 中嵌入业务逻辑,而不是更学术的问题 :)
  • @Affe 哦,这甚至不是一个真正的 Web 应用程序,它只是软件测试课上给出的一个学术示例 :) 但我不认为这个错误是故意的..
  • This 相关

标签: java inheritance equals


【解决方案1】:

直接比较实例变量,而不是将实例变量与其相关的 getter 方法进行比较。

例如,改变

&& price == item.getPrice())

&& this.price == item.price)

getter 方法是不必要的,因为私有实例变量只能在类结构之外访问。


注意:

我之前推荐了以下内容:

&& this.getPrice() == item.getPrice())

虽然它适用于问题的示例,但它并不适合所有情况。考虑子类DiscountedItem是否声明了方法getPrice

@Override
public float getPrice() {
    return Math.floor(super.getPrice());
} 

这会导致错误的等价:

DiscountedItem firstItem = DiscountedItem(1, "", 1.1, "");
DiscountedItem secondItem = DiscountedItem(1, "", 1.0, "");
firstItem.equals(secondItem); // Returns true despite different prices.

【讨论】:

  • 我同意@Floegipoky。应该直接比较类的成员变量,而不是比较 getter 方法的结果。在比较它们的方法时,即使它们的变量值不相等,也可以让两个对象相等。我会相应地更新我的答案。
【解决方案2】:

equals 的实现应该依赖于状态和类型,而不是功能。你的基类出错了:

@Override
public boolean equals(Object object){
    if(object instanceof Item){ // Type check- good!
        Item item = (Item) object;
        if( id == item.getID()  // Depends on functionality- bad!
            && price == item.getPrice()) // Depends on functionality- bad!                    
        { return true; }
    }
    return false;
}

item.getID()item.getPrice(),正如您所注意到的,可以被覆盖以破坏 Item.equals() 的合同。

@Override
public boolean equals(Object object){
    if(object instanceof Item){ // Type check- good!
        Item item = (Item) object;
        if( id == item.id  // Depends on state- good!
            && price == item.price)  // Depends on state- good!
        { return true; }
    }
    return false;
}

这永远不会被子类破坏。此外,它使孩子能够有意义地委派给它。

@Override
public boolean equals(Object object) {
    if(object instanceof DiscountedItem){
        DiscountedItem item = (DiscountedItem) object;
        return (super.equals(item)
                && this.discount == item.discount
        );
    }
    return false;
}

孩子只需要担心比较它拥有的数据。

【讨论】:

  • 感谢您的回答,您当然是对的!通常,当我编写代码时,我会按照您的建议进行操作,但是直到现在我才发现它可能有多么错误。但是为了记录,我没有编写此代码,我只是应该对其进行测试.再次感谢您!
【解决方案3】:

哦,天哪,我想我只需要发布问题就可以得到它..

要摆脱这个问题,而不是直接访问变量 - 调用 getter!

@Override
public boolean equals(Object object){
      if(object instanceof Item){
          Item item = (Item) object;
          if( this.getID() == item.getID()
              && this.getPrice() == item.getPrice())                    
          { return true; }
      }
      return false;
}

此代码在覆盖方法时不再有问题。

【讨论】:

    【解决方案4】:

    如果调用 DiscountedItem 的 equals 函数,什么时候会出现问题? 如果某物是 DiscountedItem 而其他物是 Item 这两个 永远不会平等。对?所以我不认为你有问题 总是调用 getter。

    另外,如果你覆盖equals,你需要覆盖hashCode。

    【讨论】:

    • 我不是要比较不同的对象,而是比较从父对象继承的对象的一部分。 hashCode 已实现,但我没有包含它,因为它与问题无关..
    • 对,是的,我的意思是这些“对象的一部分”总是“来自后代类的对象的一部分”,所以我说我认为这没有问题(关于覆盖方法)。
    猜你喜欢
    • 2021-05-04
    • 1970-01-01
    • 1970-01-01
    • 2019-12-09
    • 2012-11-20
    • 1970-01-01
    • 2013-03-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多