首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >理解关于类的FindBugs警告不会覆盖超类中的等于

理解关于类的FindBugs警告不会覆盖超类中的等于
EN

Stack Overflow用户
提问于 2012-07-27 10:22:48
回答 3查看 5.6K关注 0票数 3

据Java中的乔希·布洛赫( Josh Bloch )说:

除非您愿意放弃面向对象抽象的好处,否则无法扩展可实例化的类并在保留等于契约的同时添加值组件。

举个例子,我有一个类Foo,它覆盖了由Intellij实现的equals()hashcode()。现在,我有了另一个类FooChild,它扩展了Foo,并在'Foo`‘中增加了几个字段。现在FindBugs正在抱怨FooChild:

类不覆盖超类中的“等于”类--该类扩展了定义“等于”方法并添加字段的类,但没有定义“等于”方法本身。因此,该类实例的相等性将忽略子类和添加字段的标识。请确保这是要实现的,并且不需要覆盖equals方法。即使您不需要重写等于方法,也可以考虑重写它,以记录子类的equals方法只返回调用super.equals(o)的结果这一事实。

我的问题是“该类实例上的相等意味着什么将忽略子类的标识?我理解忽略添加的字段的部分,因为还没有为它们编写equals()方法。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-07-27 11:50:09

什么是“这个类的实例上的相等将忽略子类的标识?”

如果类Foo有一个equals()方法,那么FooChild继承它,这意味着如果您使用equals()比较FooChild的两个实例,那么将调用Foo.equals()方法。

如果FooChild有任何数据成员,那么FooChild的两个实例可能对其Foo部件中的成员具有相同的值,但对于在类中直接定义的成员则有不同的值。但是Foo.equals()方法只会查看在Foo中定义的成员,因此会将两个这样的对象发音为equals(),尽管它们的FooChild部分是不同的。

这就是为什么您需要在equals()中覆盖FooChild

现在,如果您使用FooFooChild比较equals(),当两个类都有自己的equals()版本时,会发生什么?这取决于您在哪个对象上调用equals(),以及如何实现这两个equals()方法。坦白说,这真是一团糟!这就是第一句话的意思,是乔希·布洛赫的话。定义这两个equals()方法是不可能的,因此它们总是做正确的事情。因此,最好避免一个值类(即标识与其成员变量的值绑定的类)扩展另一个值类的情况。

票数 6
EN

Stack Overflow用户

发布于 2012-07-27 10:32:10

首先,在计算equalshashCode方法时,不应该包含来自超类的任何状态。该状态超出了您正在处理的类的范围,您的类应该依赖于超类,而不是重写其功能。

当使用equalshashCode生成IntelliJ时,如果您想包含来自父类的任何计算,只需对添加对super.equals(o)super.hashCode()的调用的方法进行小修改即可。

实现这些方法的另一种方法是使用来自EqualsBuilder库的HashCodeBuilder阿帕奇公域朗库,这些构建器提供了添加超级调用的语义。

编辑:

回答“这个类的实例上的相等意味着什么将忽略子类的标识?”:正如您已经指出的,这意味着如果您有两个类,一个扩展另一个,并且子类既不覆盖equals也不覆盖hashCode,那么,当检查两个实例是否相等(或者添加到散列结构)时,它们的行为都会像来自同一个类一样。下面是一个示例:

代码语言:javascript
复制
class A {
   private int intField = 2;

   public A(int value) {
       intField = value;
   }

   public boolean equals(Object o) {
       if (null == o) return false;
       if (this == o) return true;
       if (!(o instanceof A)) return false;

       return intField == ((A) o).intField;
   }

   public int hashCode() {
       return 11 * intField;
   }
}

class B extends A {
   private boolean boolField = true;

   public B(int intValue, boolean boolValue) {
       super(intValue);
       boolField = boolValue;
   }

   // no equals or hashCode
}

因此,有了这些类,您将面临以下问题:

代码语言:javascript
复制
A a = new A(12);
B b = new B(12, false);
b.equals(a);    // returns true

为了避免这种情况,只需在equalshashCode方法中在IntelliJ中生成它们之后添加一个超级调用。

代码语言:javascript
复制
class B extends A {
   private boolean boolField = true;

   public B(int intValue, boolean boolValue) {
       super(intValue);
       boolField = boolValue;
   }

   public boolean equals(Object o) {
       if (null == o) return false;
       if (this == o) return true;
       if (!(o instanceof B)) return false;
       if (!super.equals(o)) return false;

       return boolField == ((B) o).boolField;
   }

   public int hashCode() {
       int hash = super.hashCode();
       hash += 11 * Boolean.valueOf(boolField).hashCode();
       return hash;
   }

}

另一种方法,正如我已经说过的,是使用库中的构建器。

票数 3
EN

Stack Overflow用户

发布于 2012-07-27 12:04:46

处理这类问题的通常方法是诉诸于构图而不是继承。这是必要的,因为正如乔希·布洛克在其出色的著作“有效的Java”中详细介绍的那样,您不能真正覆盖equals(Object)的实现,它考虑属性而不破坏equals(Object)的一般契约。

示例:

代码语言:javascript
复制
class Foo {
    boolean property;

    public boolean equals(Object that) {
        return this == that
            || (that instanceof Foo)
                && this.property == ((Foo) that).property;
    }

    public int hashCode() { ... } // needs to be consistent with equals(Object).
}

class Bar extends Foo {
    boolean anotherProperty;

    // This is broken - DO NOT USE IT!
    public boolean equals(Object that) {
        return super.equals(that)
            && (that instanceof Bar)
            && this.anotherProperty == ((Bar) that).anotherProperty
    }

    public int hashCode() { ... } // needs to be consistent with equals(Object).
}

现在,为什么Bar.equals(对象)的实现会中断呢?仅仅是因为它破坏了对称性!这是一个证明:

代码语言:javascript
复制
Foo foo = new Foo();
Bar bar = new Bar();
assert foo.equals(bar); // passes because bar is a Foo and property is zero
assert bar.equals(foo); // throws up because foo isn't an instance of Bar!

这种问题不能通过继承来解决。你需要用构图。

因此,在重写相等(对象)和hashCode()方法时声明这些方法通常是一个好主意,这样它们就不会再次被破坏的实现覆盖。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11685914

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档