Any Reason to Prefer Getclass() Over Instanceof When Generating .Equals()

Any reason to prefer getClass() over instanceof when generating .equals()?

If you use instanceof, making your equals implementation final will preserve the symmetry contract of the method: x.equals(y) == y.equals(x). If final seems restrictive, carefully examine your notion of object equivalence to make sure that your overriding implementations fully maintain the contract established by the Object class.


What I'm trying to get at here is that if you believe getClass() is the only reliable way to preserve symmetry, you are probably using equals() the wrong way.

Sure, it's easy to use getClass() to preserve the symmetry required of equals(), but only because x.equals(y) and y.equals(x) are always false. Liskov substitutability would encourage you to find a symmetry-preserving implementation that can yield true when it makes sense. If a subclass has a radically different notion of equality, is it really a subclass?

instanceof Vs getClass( )

The reason that the performance of instanceof and getClass() == ... is different is that they are doing different things.

  • instanceof tests whether the object reference on the left-hand side (LHS) is an instance of the type on the right-hand side (RHS) or some subtype.

  • getClass() == ... tests whether the types are identical.

So the recommendation is to ignore the performance issue and use the alternative that gives you the answer that you need.

Is using the instanceOf operator bad practice ?

Not necessarily. Overuse of either instanceOf or getClass() may be "design smell". If you are not careful, you end up with a design where the addition of new subclasses results in a significant amount of code reworking. In most situations, the preferred approach is to use polymorphism.

However, there are cases where these are NOT "design smell". For example, in equals(Object) you need to test the actual type of the argument, and return false if it doesn't match. This is best done using getClass().


Terms like "best practice", "bad practice", "design smell", "antipattern" and so on should be used sparingly and treated with suspicion. They encourage black-or-white thinking. It is better to make your judgements in context, rather than based purely on dogma; e.g. something that someone said is "best practice". I recommend that everyone read No Best Practices if they haven't already done so.

Java .equals() instanceof subclass? Why not call superclass equals instead of making it final?

A lot of the examples use instanceof for two reasons: a) it folds the null check and type check into one or b) the example is for Hibernate or some other code-rewriting framework.

The "correct" (as per the JavaDoc) solution is to use this.getClass() == obj.getClass(). This works for Java because classes are singletons and the VM guarantees this. If you're paranoid, you can use this.getClass().equals(obj.getClass()) but the two are really equivalent.

This works most of the time. But sometimes, Java frameworks need to do "clever" things with the byte code. This usually means they create a subtype automatically. Since the subtype should be considered equal to the original type, equals() must be implemented in the "wrong" way but this doesn't matter since at runtime, the subtypes will all follow certain patterns. For example, they will do additional stuff before a setter is being called. This has no effect on the "equalness".

As you noticed, things start to get ugly when you have both cases: You really extend the base types and you mix that with automatic subtype generation. If you do that, you must make sure that you never use non-leaf types.

Why does the equals() implementation generated by Eclipse check for null before type checking (instanceof)?

It is unnecessary because instanceof has a built in null check.
But instanceof is a lot more than a simple foo == null. It is a full instruction preparing a class check doing unnecessary work before the null check is done. (see for more details http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.instanceof)

So a separate null check could be a performance improvement.
Did a quick measurement and no surprise foo==null is faster than a nullcheck with instanceof.

But usually you do not have a ton of nulls in an equals() leaving you with a duplicate unnecessary nullcheck most of the times... which will likely eat up any improvement made during null comparisons.

My conclusion: It is unnecessary.

Code used for testing for completeness (remember to use -Djava.compiler=NONE else you will only measure the power of java):

public class InstanceOfTest {
public static void main(String[] args) {
Object nullObject = null;

long start = System.nanoTime();
for(int i = Integer.MAX_VALUE; i > 0; i--) {
if (nullObject instanceof InstanceOfTest) {}
}
long timeused = System.nanoTime() - start;

long start2 = System.nanoTime();
for(int i = Integer.MAX_VALUE; i > 0; i--) {
if (nullObject == null) {}
}
long timeused2 = System.nanoTime() - start2;

System.out.println("instanceof");
System.out.println(timeused);
System.out.println("nullcheck");
System.out.println(timeused2);
}
}

Java @Override equals(): When this.getClass() != o.getClass() fails but shouldn't

happy songs correctly stated in his response:

myObject2 is an instance of a proxy class, generated at runtime by Hibernate using Byte Buddy. The generated proxy intercepts all method invocations, that's why getClass() returns different results.

I really didn't want to use instanceof because that is considered bad practice so I started poking around and stumbled onto a post having a similar issue. Their solution was to add the final keyword to their class declaration. I thought this insignificant but gave it try - AND IT WORKED! Adding the final keyword caused

if ( o == null || this.getClass() != o.getClass() )

and

if ( o == null || !this.getClass().equals( o.getClass() ) )

to work properly. My class code is now:

@Entity( name = "MyClass" )
@Table( name = "my_class" )
final public class MyClass extends MySuperClass
{
...
@Override
public boolean equals( Object o )
{
if ( this == o )
{
return true;
}
if ( o == null || !this.getClass().equals( o.getClass() ) )
{
return false;
}
if ( !super.equals( o ) )
{
return false;
}
MyClass that = ( MyClass ) o;
return this.var1.equals( that.var1 ) && this.var2.equals( that.var2 );
}
...
}

Thank you all for your assistance! And a big thank you to happy songs for pointing me in the proper direction!

What should be the result of overridden Object#equals(Object) when instance is upcasted?

It turns out, the problem is a bit deeper than I originally thought for one main reason: Liskov Subsitution Principle.

My PurchaseOrder#equals(Object) method violates LSP. Because InternationalPurchaseOrder extends PurchaseOrder, it is correct to say that an international purchase IS-A purchase order. Therefore, to comply with LSP, I should be able to replace one instance for the other without any ill effects. Because of this, the line if (getClass() != obj.getClass()) return false; completely violates this principle.

Even when my focus was around upcasting (which by now I am pretty much convinced that it is a code smell), almost the same issue applies: Should I be able to compare instances of PurchaseOrder and InternationalPurchaseOrder and return true if they contain the same data internally? According to Joshua Bloch book Effective Java, Item 10 (Obey the general contract when overriding equals), the answer should be yes, but it is indeed no; but not for the reasons I initially thought (they are not the same type). The problem is this, and I quote:

There is no way to extend an instantiable class and add value component while preserving the equals contract, unless you're willing to forgo the benefits of object-oriented abstraction.

He continues to get in more details about this. The conclusion is simple: Do it this way and loose the benefits of abstraction, and violate LSP in the process. Or... simply follow a very important programming principle (especially in Java): favor composition over inheritance and inject the attributes you need to make a PurchaseOrder domestic or international in this example. In my case, this solution will look like this (irrelevant details of class omitted):

public class InternationalPurchaseOrder {
private PurchaseOrder po;
private String country;

public (PurchaseOrder po, String country) {
this.po = po;
this.country = country;
}

public PurchaseOrder asPurchaseOrder() {
return po;
}

@Override
public boolean equals (Object obj) {
if (!(obj instanceof InternationalPurchaseOrder)) {
return false;
}

InternationalPurchaseOrder ipo = (InternationalPurchaseOrder)obj;
return ipo.po.equals(po) && cp.country.equals(country);
}
}

This way, an InternationalPurchaseOrdercan continue to be compared to other instances of the same class AND if needed to be compared to objects of PurchaseOrder type, all that is needed is to call the asPurchaseOrder to return an object of the compatible type.

How to implement equals with hibernate without risking losing the symmetric property?

After rading up some more i summarize this question with:

  • Use instanceof and you can never add significant members to subclasses.(
    There is no way to extend an instantiable class and add a value component while preserving the equals contract (Bloch)
  • Use getClass and you violate the Liskov substitution principle

Langers says http://www.angelikalanger.com/Articles/JavaSolutions/SecretsOfEquals/Equals.html

  • The instanceof test is correct only for final classes or if at least method equals() is final in a superclass. The latter essentially
    implies that no subclass must extend the superclass's state, but can
    only add functionality or fields that are irrelevant for the object's
    state and behavior, such as transient or static fields.

Implementations using the getClass() test on the other hand always
comply to the equals() contract; they are correct and robust. They
are, however, semantically very different from implementations that
use the instanceof test. Implementations using getClass() do not allow
comparison of sub- with superclass objects, not even when the subclass
does not add any fields and would not even want to override equals() .
Such a "trivial" class extension would for instance be the addition of
a debug-print method in a subclass defined for exactly this "trivial"
purpose. If the superclass prohibits mixed-type comparison via the
getClass() check, then the trivial extension would not be comparable
to its superclass. Whether or not this is a problem fully depends on
the semantics of the class and the purpose of the extension.

Summary - use instanceof with final for equals avoids breaking symmetry, and avoids the hibernate "proxy" problem.

Links

  • Liskov and hibernate
  • GetClass vs instanceof in equals


Related Topics



Leave a reply



Submit