在生成. equals ()时,有什么理由更喜欢 getClass ()而不是 instanceof 吗?

我使用 Eclipse 来生成 .equals().hashCode(),并且有一个标签为“使用‘ instanceof’来比较类型”的选项。默认情况下,取消选中此选项并使用 .getClass()比较类型。有什么原因我应该更喜欢 .getClass()超过 instanceof

不使用 instanceof:

if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;

使用 instanceof:

if (obj == null)
return false;
if (!(obj instanceof MyClass))
return false;

我通常检查 instanceof选项,然后进入并删除“ if (obj == null)”检查。(这是多余的,因为空对象将始终失败 instanceof。)有什么理由说这是个坏主意吗?

137360 次浏览

如果希望确保只匹配该类,则使用 getClass() ==。如果你想匹配子类,那么 instanceof是必需的。

而且,instanceof 不会与 null 匹配,但是可以安全地与 null 进行比较。所以你不需要检查它。

if ( ! (obj instanceof MyClass) ) { return false; }

这取决于您是否考虑给定类的子类是否等于它的父类。

class LastName
{
(...)
}




class FamilyName
extends LastName
{
(..)
}

在这里我将使用“ instanceof”,因为我希望将 LastName 与 FamilyName 进行比较

class Organism
{
}


class Gorilla extends Organism
{
}

这里我将使用“ getClass”,因为类已经说明了这两个实例是不等价的。

Instanceof 适用于同一类 或者及其子类的实例

您可以使用它来测试对象是类的实例、子类的实例还是实现特定接口的类的实例。

ArryaList 和 RoleList 都是 Instanceof List

同时

GetClass () = = o.getClass () 仅当两个对象(this 和 o)属于完全相同的类时才为 true。

所以根据你需要比较的内容,你可以选择其中一个。

如果你的逻辑是: “一个对象只有在它们都是同一个类的情况下才等于另一个对象”,那么你应该选择“等于”,我认为在大多数情况下都是这样。

如果您使用 instanceof,使您的 equals实现 final将保留方法的对称契约: x.equals(y) == y.equals(x)。如果 final似乎是限制性的,那么请仔细检查对象等价的概念,以确保重写实现完全维护由 Object类建立的契约。


我想说的是,如果你认为 getClass()是保持对称性的唯一可靠方法,那么你可能使用 equals()的方法是错误的。

当然,使用 getClass()来保持 equals()所需的对称性是很容易的,但这仅仅是因为 x.equals(y)y.equals(x)总是假的。Liskov 可替换性将鼓励您找到一个保持对称性的实现,它可以在有意义的时候生成 true。如果一个子类有一个完全不同的相等概念,那么它真的是一个子类吗?

使用 getClass的原因是为了确保 equals协议的对称属性:

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

通过使用 instanceof,可能不是对称的: 狗伸展动物。 动物的 equals对动物进行 instanceof检查。 狗的 equals对狗进行 instanceof检查。 给动物 和狗 D(其他字段相同) :

a.equals(d) --> true
d.equals(a) --> false

这违反了对称属性。

为了严格遵守平等契约,必须确保对称性,因此阶级需要相同。

Angelika Langers = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =对一些常见和著名的例子进行了长时间和详细的讨论,其中包括 Josh Bloch 和 Barbara Liskov,他们在大多数例子中发现了一些问题。她还进入了 instanceofgetClass。引用一下

结论

剖析了四个任意选择的 equals ()实现例子之后,我们得出了什么结论?

首先: 在 equals ()的实现中,执行类型匹配检查有两种截然不同的方法。类可以通过 instanceof 运算符允许超类和子类对象之间的混合类型比较,或者类可以通过 getClass ()测试将不同类型的对象视为不相等的对象。上面的例子很好地说明了使用 getClass ()的 equals ()实现通常比使用 instanceof 的实现更健壮。

Instanceof 测试仅对于 final 类是正确的,或者至少在超类中方法 equals ()是 final 的。后者本质上意味着没有子类必须扩展超类的状态,但只能添加与对象的状态和行为无关的功能或字段,例如瞬态或静态字段。

另一方面,使用 getClass ()测试的实现总是遵守 equals ()契约; 它们是正确和健壮的。但是,它们在语义上与使用 instanceof 测试的实现非常不同。使用 getClass ()的实现不允许子类与超类对象进行比较,即使子类不添加任何字段,甚至不希望重写 equals () ,也不允许这样做。例如,这样一个“平凡的”类扩展就是在为这个“平凡的”目的定义的子类中添加一个调试-打印方法。如果超类禁止通过 getClass ()检查进行混合类型比较,那么微不足道的扩展将无法与其超类进行比较。这是否是一个问题完全取决于类的语义和扩展的目的。

这两种方法都有自己的问题。

如果子类改变了标识,那么您需要比较它们的实际类。否则你就违反了对称属性。例如,不同类型的 Person不应被认为是等价的,即使它们具有相同的名称。

但是,有些子类不改变标识,这些子类需要使用 instanceof。例如,如果我们有一堆不可变的 Shape对象,那么长度和宽度为1的 Rectangle应该等于单位 Square

在实践中,我认为前一种情况更有可能是正确的。通常,子类化是你身份的一个基本组成部分,和你的父类一模一样,除了你可以做一件小事,这并不能使你平等。

这是一场宗教辩论,两种方法都有自己的问题。

  • 使用 Instanceof,您永远不能向子类添加重要成员。
  • 使用 GetClass就违反了 Liskov代换原则。

Bloch 有效的 Java 第二版中还有另一条相关建议:

  • 项目17: 设计和编写继承或禁止继承的文件

如果我错了请纠正我,但是如果您想确保您的实例不是您正在比较的类的子类,getClass ()将非常有用。如果你在这种情况下使用 instanceof,你不可能知道,因为:

class A { }


class B extends A { }


Object oA = new A();
Object oB = new B();


oA instanceof A => true
oA instanceof B => false
oB instanceof A => true // <================ HERE
oB instanceof B => true


oA.getClass().equals(A.class) => true
oA.getClass().equals(B.class) => false
oB.getClass().equals(A.class) => false // <===============HERE
oB.getClass().equals(B.class) => true

实际上 instanceof 检查对象是否属于某个层次结构。汽车对象属于车辆类。因此,“ new Car () instance of Vehical”返回 true。还有“新车”。GetClass ().“ return false,虽然 Car 对象属于 Vehical 类,但它被归类为一个单独的类型。