生成.equals()时比get instance()优先于instanceof的任何理由?

我使用Eclipse来生成.equals().hashCode() ,并且有一个标签为“使用'instanceof'来比较types”的选项。 默认情况下,此选项.getClass()选中,并使用.getClass()来比较types。 有没有什么理由我应该喜欢.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) ”检查。 (这是多余的,因为null对象总是会失败instanceof )是否有任何理由是一个坏主意?

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

乔希布洛赫赞成你的方法:

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

另请参阅这个答案 。

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

Angelika Langers 平等的秘密通过对一些常见和众所周知的例子进行了详细的讨论,其中包括Josh Bloch和Barbara Liskov,他们发现了大部分问题。 她也进入了instanceof vs getClass 。 有些引用它

结论

parsing了equals()的四个任意select的实现的例子,我们得出什么结论?

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

testof的instanceof只对最终的类是正确的,或者如果equals()在超类中是final的。 后者基本上意味着没有任何子类必须扩展超类的状态,但只能添加与对象的状态和行为无关的function或字段,例如瞬态或静态字段。

另一方面,使用getClass()testing的实现总是遵从equals()合约; 他们是正确和强大的。 但是,它们与使用instanceoftesting的实现在语义上非常不同。 使用getClass()的实现不允许将子类与超类对象进行比较,即使子类不添加任何字段,甚至不想重写equals()。 这种“平凡的”类扩展例如是在为这个“平凡的”目的定义的子类中添加debugging打印方法。 如果超类禁止通过getClass()检查进行混合types比较,那么平凡的扩展将不能与其超类相提并论。 这个问题是否完全取决于类的语义和扩展的目的。

使用getClass的原因是为了确保equals契约的对称性。 从等于'JavaDocs:

它是对称的:对于任何非空引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)才返回true。

通过使用instanceof,可能不是对称的。 考虑一下这个例子:狗扩展了动物。 动物的equals是动物检查的一个instanceof 。 狗的equals做狗的一个instanceof检查。 给予动物和狗d (与其他领域相同):

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

这违反了对称性。

要严格遵循平等的合同,必须保证对称性,因此class级需要保持一致。

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

  • 使用instanceof ,你永远不能将重要的成员添加到子类。
  • 使用getClass ,你违反了Liskovreplace原则。

Bloch在Effective Java第二版中提供了另一个相关的build议:

  • 第17项:inheritance的devise和文件或禁止

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

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

 class LastName { (...) } class FamilyName extends LastName { (..) } 

这里我会使用'instanceof',因为我想要一个姓氏与FamilyName进行比较

 class Organism { } class Gorilla extends Organism { } 

在这里我会使用'getClass',因为这个类已经说过这两个实例是不等价的。

如果你想确保只有这个类匹配,那么使用getClass() == 。 如果你想匹配子类,那么instanceof是需要的。

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

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

instanceof适用于同一类或其子类的语句

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

ArryaList和RoleList都是instanceofList

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

所以根据你需要比较你可以使用一个或另一个。

如果你的逻辑是:“一个对象与其他对等只有当他们是同一个class级”你应该去“等于”,我认为是大多数情况下。

两种方法都有他们的问题。

如果子类改变身份,那么你需要比较他们的实际类。 否则,你违反了对称性。 例如,不同types的Person不应该被认为是等同的,即使他们有相同的名字。

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

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

其实instanceof检查对象是否属于某个层次结构。 例如:汽车对象属于汽车类。 因此“Vehical的新Car()实例”返回true。 和“new Car()。getClass()。equals(Vehical.class))”返回false,虽然Car对象属于Vehical类,但它被分类为一个单独的types。