我正在使用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
。)有什么理由不好吗?
答案 0 :(得分:167)
Josh Bloch赞成你的方法:
我赞成
instanceof
方法的原因是当你使用getClass
方法时,你有一个限制,即对象只等于同一个类的其他对象,相同的运行时类型。如果扩展一个类并为它添加几个无害的方法,那么检查一下子类的某个对象是否等于超类的对象,即使对象在所有重要方面都相同,你也会得到令人惊讶的答案是他们并不平等。实际上,这违反了对 Liskov替换原则的严格解释,并且可能导致非常令人惊讶的行为。在Java中,它特别重要,因为大多数集合(HashTable
等)都基于equals方法。如果将超类的成员作为键放在哈希表中,然后使用子类实例查找它,则无法找到它,因为它们不相等。
有效的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)
这是一场宗教辩论。这两种方法都存在问题。
Bloch在Effective Java Second Edition中有另一条相关建议:
答案 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类,但它被归类为单独的类型。