当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《有没有理由在生成.equals()时更喜欢getClass()而不是instanceof?》经验,为你挑选了8个好方法。

我正在使用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.)有什么理由不好吗?



1> Michael Myer..:

Josh Bloch喜欢你的方法:

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

另见这个SO答案.

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


+1每个做java的人都应该读这本书至少10次!
instanceof方法的问题在于它打破了Object.equals()的"对称"属性,因为x.equals(y)== true,但是如果x.getClass,则y.equals(x)== false ()!= y.getClass().除非绝对必要,否则我不打算破坏此属性(即,覆盖托管或代理对象的equals()).
当且仅当基类定义子类对象之间的相等性时,instanceof方法才是正确的.使用`getClass`并不违反LSP,因为LSP只涉及现有实例可以做什么 - 而不是可以构造什么类型的实例.`getClass`返回的类是对象实例的不可变属性.LSP并不意味着应该可以创建一个子类,其中该属性指示除创建它之外的任何类.

2> erickson..:

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


绝对是关键的决定.对称性必须适用,而instanceof使得非常容易意外地不对称

3> Johannes Sch..:

Angelika Langers 平等的秘密通过对一些常见和众所周知的例子进行了长时间的详细讨论,其中包括Josh Bloch和Barbara Liskov,他们发现了大多数问题.她还进入了instanceofVS getClass.有人引用它

结论

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

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

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

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



4> Steve Kuo..:

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

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

通过使用instanceof,可能不是对称的.考虑一下这个例子:Dog extends Animal.动物equals做的instanceof动物的检查.狗的equals做一个instanceof犬检查.给动物a和狗d(与其他领域相同):

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

这违反了对称属性.

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


这是这个问题的第一个简明扼要的答案.代码示例胜过千言万语.

5> McDowell..:

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

使用instanceof,您永远不能将重要成员添加到子类.

使用getClass并违反Liskov替换原则.

Bloch在Effective Java Second Edition中有另一个相关的建议:

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


两个不同类的实例应该自己报告的唯一时间是相同的,如果它们从公共基类或接口继承,它定义了什么相等意味着[Java应用的哲学,可疑恕我直言,某些集合接口].除非基类合同说`getClass()`不应该被认为有意义,超出了有问题的类可以转换为基类的事实,`getClass()`的返回应该被视为必须匹配的任何其他属性实例是平等的.
"设计和文件继承或禁止它"我认为非常重要.我的理解是,你应该覆盖equals的唯一时间是你是否正在创建一个不可变的值类型,在这种情况下,类应该被声明为final.由于没有继承,您可以根据需要自由实现equals.在允许继承的情况下,对象应通过引用相等进行比较.

6> TraderJoeChi..:

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



7> Clint..:

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

同样,instanceof将不会与null匹配,但可以安全地与null进行比较。因此,您不必进行空检查。

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



8> Pierre..:

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

class LastName
{
(...)
}


class FamilyName
extends LastName
{
(..)
}

在这里我将使用'instanceof',因为我希望将姓氏与FamilyName进行比较

class Organism
{
}

class Gorilla extends Organism
{
}

在这里,我将使用“ getClass”,因为该类已经说过这两个实例并不等效。

推荐阅读
ERIK又
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有