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

时间:2009-02-27 20:14:37

标签: java eclipse equals 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。)有什么理由不好吗?

11 个答案:

答案 0 :(得分:167)

Josh Bloch赞成你的方法:

  

我赞成instanceof方法的原因是当你使用getClass方法时,你有一个限制,即对象只等于同一个类的其他对象,相同的运行时类型。如果扩展一个类并为它添加几个无害的方法,那么检查一下子类的某个对象是否等于超类的对象,即使对象在所有重要方面都相同,你也会得到令人惊讶的答案是他们并不平等。实际上,这违反了对 Liskov替换原则的严格解释,并且可能导致非常令人惊讶的行为。在Java中,它特别重要,因为大多数集合(HashTable等)都基于equals方法。如果将超类的成员作为键放在哈希表中,然后使用子类实例查找它,则无法找到它,因为它们不相等。

另见this SO answer

有效的Java chapter 3也涵盖了这一点。

答案 1 :(得分:94)

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

答案 2 :(得分:62)

Angelika Langers Secrets of equals通过对一些常见和众所周知的例子进行了长时间的详细讨论,其中包括Josh Bloch和Barbara Liskov,他们发现了大多数问题。她也进入instanceof vs getClass。有人引用它

  

结论

     

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

     

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

     

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

     

另一方面,使用getClass()测试的实现总是符合equals()契约;他们是正确和强大的。但是,它们在语义上与使用instanceof测试的实现非常不同。使用getClass()的实现不允许比较子类和超类对象,即使子类没有添加任何字段,甚至不想重写equals()。这样一个“普通的”类扩展例如是在为这个“普通”目的而定义的子类中添加调试打印方法。如果超类通过getClass()检查禁止混合类型比较,那么普通扩展将无法与其超类相比。这是否完全取决于类的语义和扩展的目的。

答案 3 :(得分:55)

使用getClass的原因是为了确保equals合同的对称属性。来自equals的JavaDocs:

  

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

通过使用instanceof,可能不是对称的。考虑这个例子: 狗延伸动物。 动物equals对动物进行instanceof检查。 狗的equals对狗进行instanceof检查。 给动物 a 和狗 d (与其他字段相同):

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

这违反了对称属性。

要严格遵守平等合同,必须确保对称性,因此课程必须相同。

答案 4 :(得分:26)

这是一场宗教辩论。这两种方法都存在问题。

  • 使用 instanceof ,您永远不能将重要成员添加到子类。
  • 使用 getClass 并违反Liskov替换原则。

BlochEffective Java Second Edition中有另一条相关建议:

  • 第17项:继承的设计和文件或禁止它

答案 5 :(得分:21)

如果我错了,请纠正我,但是当你想确保你的实例不是你要比较的类的子类时,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

答案 6 :(得分:5)

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

class LastName
{
(...)
}


class FamilyName
extends LastName
{
(..)
}

这里我会使用'instanceof',因为我想将LastName与FamilyName进行比较

class Organism
{
}

class Gorilla extends Organism
{
}

这里我会使用'getClass',因为该类已经说过这两个实例不相等。

答案 7 :(得分:4)

如果您想确保只有该类匹配,请使用getClass() ==。如果要匹配子类,则需要instanceof

此外,instanceof不会与null匹配,但可以安全地与null进行比较。所以你不必空检查它。

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

答案 8 :(得分:3)

instanceof 适用于同一类其子类的内容

  

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

ArryaList和RoleList都是 instanceof 列表

虽然

仅当两个对象(this和o)属于完全相同的类时,

getClass()== o.getClass()才会为真。

因此,根据您需要比较的内容,您可以使用其中一种。

如果你的逻辑是:“只有当一个对象属于同一个类时才与其他对象相等”,你应该选择“等于”,我认为这是大多数情况。

答案 9 :(得分:2)

这两种方法都存在问题。

如果子类更改了标识,则需要比较它们的实际类。否则,您违反了对称属性。例如,不同类型的Person不应被视为等效,即使它们具有相同的名称。

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

在实践中,我认为前一种情况更可能是真的。通常,子类化是你身份的一个基本部分,与你的父母完全一样,除了你可以做一件小事并不能让你平等。

答案 10 :(得分:-1)

实际上,instanceof检查对象属于某个层次结构的位置。例:汽车对象属于Vehical类。所以“Vehical的新Car()实例”返回true。并且“new Car()。getClass()。equals(Vehical.class)”返回false,尽管Car对象属于Vehical类,但它被归类为单独的类型。