• Post Reply Bookmark Topic Watch Topic
  • New Topic

equals method question  RSS feed

 
Albert Park
Greenhorn
Posts: 28
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hello I am going over Core java book.
Here is the code that I don't understand.



What is the differecen between (this == otherObject) and (getClass() != otherObject.getClass())?
If I am correct, the first condition checks the reference of two objects and the second condition checks if both objects belong to the same class.
From my understand, if an object has a same reference as another, then can't we safely assume that they are from same class(Is this to check if the object is belong to a subclass)?
Please let me know how they differ and if not, why do we have to check the same condition twice?

Thanks.
 
Mansukhdeep Thind
Ranch Hand
Posts: 1163
Eclipse IDE Firefox Browser Java
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Albert Park wrote: From my understand, if an object has a same reference as another, then can't we safely assume that they are from same class. Is this to check if the object is belong to a subclass?


I can say that a List reference points to an ArrayList object, or a Vector object or a LinkedList object. How do you know for sure that a List reference is not pointing to a collection which is actually a LinkedList or an ArrayList? The object coming in to the equals() method is simply an Object. Correct? So how do you know for sure whether even you can do this without throwing a ClassCastException:



Hence, that class check is mandatory to make sure that they belong to the same class. Only then can you define when should 2 employees be considered equal.
 
Albert Park
Greenhorn
Posts: 28
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
ahh. Got it.

Thanks!
 
Mansukhdeep Thind
Ranch Hand
Posts: 1163
Eclipse IDE Firefox Browser Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You are always welcome. Having said that, it is a flawed design if you are not sure exactly what type of objects your classes hold and you have to use an instanceof check or getClass() check to make sure the type of Object you are dealing with when it comes to overriding equals() method and comparing objects for equality. When you study about the concepts of Generics in Java, you will be able to appreciate what I am hinting at.
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Albert Park wrote:If I am correct, the first condition checks the reference of two objects and the second condition checks if both objects belong to the same class.

You are correct.

From my understand, if an object has a same reference as another, then can't we safely assume that they are from same class(Is this to check if the object is belong to a subclass)?

You are incorrect. If two objects are '==', they are the same object - ergo, they must be the same class.

This is a rare exception to the rule that you should AvoidTheEqualityOperator (←click) when comparing objects.

BTW, that getClass() comparison is only one way of doing the 3rd check. My preference is to use:
if (otherObject instanceof Employee)

and here are four good reasons:
1. It's faster (and for a method that's used as often as equals() is, that can make a difference).
2. If otherObject is null it will return false, which means that you don't need the 2nd check.
3. If it passes the test, your cast will NOT produce a compiler warning.
4. It works for any subclass that doesn't override equals().
There are other reasons too, but it's probably not worth going into them now.

There are occasions when getClass() is necessary, but to be honest, I'm surprised that a book is teaching it as the standard approach.
Furthermore, if you do do that check, it really should be written:
if (! getClass().equals(otherObject.getClass()))

Winston
 
Mansukhdeep Thind
Ranch Hand
Posts: 1163
Eclipse IDE Firefox Browser Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Winston wrote: There are occasions when getClass() is necessary, but to be honest, I'm surprised that a book is teaching it as the standard approach.


I am curious which book are you studying from. If you learn the wrong methodologies in the first go as a beginner/novice, they leave an imprint on the brain that becomes difficult to erase when you actually get to the correct stuff. Better get your hands on a book which takes the correct approach.
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Mansukhdeep Thind wrote:Better get your hands on a book which takes the correct approach.

I'm not sure that "correct" is quite the right term here. There is an argument - although not a great one, IMO - for using the getClass() check, because it's harder to violate things like transitivity by accident (or by default).

Personally, I prefer the canEqual() trick - one that Josh Bloch still hasn't come around to yet (at least not in version 2 ).

Winston
 
Albert Park
Greenhorn
Posts: 28
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Mansukhdeep Thind wrote:
Winston wrote: There are occasions when getClass() is necessary, but to be honest, I'm surprised that a book is teaching it as the standard approach.


I am curious which book are you studying from. If you learn the wrong methodologies in the first go as a beginner/novice, they leave an imprint on the brain that becomes difficult to erase when you actually get to the correct stuff. Better get your hands on a book which takes the correct approach.


Hello, I am using Core Java Volume-I 8th edition.
I read good reviews from amazon.
I also went over introduction to java programming by liang.
Can you guys recommend any java books?
 
Jeff Verdegan
Bartender
Posts: 6109
6
Android IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Albert Park wrote:
If I am correct, the first condition checks the reference of two objects and the second condition checks if both objects belong to the same class.
From my understand, if an object has a same reference as another, then can't we safely assume that they are from same class(Is this to check if the object is belong to a subclass)?
Please let me know how they differ and if not, why do we have to check the same condition twice?

Thanks.


We're not checking the same condition twice. If it's the same object, then, yes, it's the same class. But as soon as we detect that it's the same object, we exit from the method and return true, so we never get to the class check.

The only time we test if it's the same class is if it's not the same object, and we're testing to see whether it's at least the same class, to determine whether it could possibly be equal, or if we should just bail out now with a false result.
 
Jeff Verdegan
Bartender
Posts: 6109
6
Android IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Mansukhdeep Thind wrote:it is a flawed design if you are not sure exactly what type of objects your classes hold and you have to use an instanceof check or getClass() check to make sure the type of Object you are dealing with when it comes to overriding equals() method and comparing objects for equality.


This is false.

In the equals() method, we must check getClass() or instanceof. We can't control who calls our equals() what arg, so we can't know ahead of time that only our class will be passed.

Which one we choose--getClass() or instanceof--depends on the semantics of our class hierarchy.

When you study about the concepts of Generics in Java, you will be able to appreciate what I am hinting at.


The equals() method does not have a type parameter, so this does not apply.
 
Jeff Verdegan
Bartender
Posts: 6109
6
Android IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Winston Gutkowski wrote:
BTW, that getClass() comparison is only one way of doing the 3rd check. My preference is to use:
if (otherObject instanceof Employee)

and here are four good reasons:
1. It's faster (and for a method that's used as often as equals() is, that can make a difference).


It is? I would expect it to be slower. getClass() only has to check one aspect of the object, but instanceof could potentially have to examine an arbitrarily long branch of the type hierarchy, no?
 
Campbell Ritchie
Marshal
Posts: 56576
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I think Jeff is right about getClass(), but you would have to check the original C++ of the JVM code to verify that. It is probably a straight return statement for a pointer to the Class<?> object, and the Class<T>#equals() method is not overridden, so it is a simple call of an == operation. Anyway, the performance difference from instanceof is likely to be minimal.
It is simply a problem with designs for inheritance; if you add fields in subclasses you are risking breaching the Liskov Substitution Principle with getClass(), and you are risking breaching the general contract of equals with instanceof. That is discussed by Odersky, in the link previously quoted, also both by Bloch and Langer.
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Jeff Verdegan wrote:It is? I would expect it to be slower.

You'd think so, wouldn't you? But all results I've seen say otherwise; and by quite a distance too.

Just ran it now over a billion calls to each, of classes obtained by masking the index to an array of differing types of object (so that the compiler hopefully can't optimize by caching results):
Event: getClass
Number of timings: 1000000000
Total elapse time: 7.252358360s
Total active time: 7.252358360s
Avg. active time: 7.252ns

Event: instanceof
Number of timings: 1000000000
Total elapse time: 3.234450280s
Total active time: 3.234450280s
Avg. active time: 3.234ns


As you can see, getClass() takes more than twice as long, and that includes all the business of retrieving the object from the array and operating the loop too (other than the check the code is identical for both). I've also run them in inverse order and the results are the same.

And basically I've seen the same thing many times before, and it struck me as odd too; which is why I remembered it.

There seems to be a slight penalty if the type is lower down the hierarchy but, for whatever reason, instanceof is very fast.

Either that or, as Josh Bloch says, reflection is just plain slow.

Winston
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Jeff Verdegan wrote:It is? I would expect it to be slower...

Just a little update for you:

I changed the objects that I store to ones that have a bit of a hierarchy to them (basically a bunch of different collections), and ran a few more tests. As you can imagine, getClass() timings were pretty much the same as above, but instanceof results were quite startling:

1. Check for a specific type (ie, one at the bottom of a hierarchy) - pretty much the same as above:
Event: instanceof HashMap
Number of timings: 1000000000
Total elapse time: 3.292775400s
Total active time: 3.292775400s
Avg. active time: 3.293ns


2. Check for an abstract type (ie, a class somewhere up the hierarchy) - slightly slower, but still quite a bit quicker than getClass():
Event: instanceof AbstractList
Number of timings: 1000000000
Total elapse time: 4.376481920s
Total active time: 4.376481920s
Avg. active time: 4.376ns


3. Check for an implemented interface - waaay slower:
Event: instanceof Map
Number of timings: 1000000000
Total elapse time: 13.226333600s
Total active time: 13.226333600s
Avg. active time: 13.226ns


What does that tell us? I have absolutely no idea, but I know you're a chap who likes this sort of stuff.

Winston

[Edit]
And just for completeness:
Event: instanceof List
Number of timings: 1000000000
Total elapse time: 16.028317640s
Total active time: 16.028317640s
Avg. active time: 16.028ns

Event: instanceof Set
Number of timings: 1000000000
Total elapse time: 17.353351280s
Total active time: 17.353351280s
Avg. active time: 17.353ns

Event: instanceof Collection
Number of timings: 1000000000
Total elapse time: 12.246886200s
Total active time: 12.246886200s
Avg. active time: 12.247ns

 
Mansukhdeep Thind
Ranch Hand
Posts: 1163
Eclipse IDE Firefox Browser Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
What does that tell us? I have absolutely no idea, but I know you're a chap who likes this sort of stuff.


It may be an indication of the way in which the elements are actually stored in a data structure. May be.
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Mansukhdeep Thind wrote:It may be an indication of the way in which the elements are actually stored in a data structure. May be.

Yes, it seems to be related in some way to "hierarchical distance", but exactly how I'm not sure. However, even with interfaces, it does seem to be able to eliminate quickly.

For example: comparison with an unrelated interface is quite quick:
Event: instanceof Number
Number of timings: 1000000000
Total elapse time: 4.824001720s
Total active time: 4.824001720s
Avg. active time: 4.824ns


Winston
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Winston Gutkowski wrote:You'd think so, wouldn't you?

Sheesh. How wrong can you (or, in this case, I) get?

My timing for getClass() turned out to have nothing to do with the method itself. In good OO fashion, my test was:
obj[i].getClass().equals(X.class)
change that to
obj[i].getClass() == X.class
and it comes down to 2.929ns (3.671 with null check).

So: virtually no difference over typed instanceof.

I feel like such a berk...

Winston
 
Jeff Verdegan
Bartender
Posts: 6109
6
Android IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks for taking the time to do those tests Winston. The "different times for different parts of the hierarchy" is pretty much what I expected. I'd also expect it to take longer on average if the object being tested implements a lot of interfaces. Basically the more potential matches that have to be examined, the longer it will take. Of course depending on the order in which the inherited types are examined, it might not have to look at all of them before it gets a true result.

In the end though, I don't think it really matters, as we wouldn't make the choice of getClass() vs. instanceof for performance reasons anyway. They have different semantics, so we'd pick the one whose semantics match what we need. I guess if the type we're testing against is a final class then the semantics become the same, and we could make the choice based on speed. It still feels like a rare case where I'd actually do that though.

But yes, you're right, I do enjoy these little diversions into the inner workings of the JVM.
 
Campbell Ritchie
Marshal
Posts: 56576
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Winston Gutkowski wrote: . . .
obj[i].getClass().equals(X.class)
change that to
obj[i].getClass() == X.class . . .
How peculiar, particularly since Class<T>#equals is not overridden from Object, and uses == internally.
 
Jeff Verdegan
Bartender
Posts: 6109
6
Android IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:
Winston Gutkowski wrote: . . .
obj[i].getClass().equals(X.class)
change that to
obj[i].getClass() == X.class . . .
How peculiar, particularly since Class<T>#equals is not overridden from Object, and uses == internally.


I suppose the difference between executing a single test/branch and doing that PLUS creating a stack frame, storing the PC, pushing the result onto the stack, popping the result, JMP-ing back to the old PC is a big difference in the relative sense. BUT I would have expected hotspot to inline that, so, yes, it is peculiar.
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Jeff Verdegan wrote:
Campbell Ritchie wrote:How peculiar, particularly since Class<T>#equals is not overridden from Object, and uses == internally.
BUT I would have expected hotspot to inline that, so, yes, it is peculiar.

I wonder if it has anything to do with the fact that Object.equals() isn't final?

Winston
 
Campbell Ritchie
Marshal
Posts: 56576
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Agree: HotSpot probably only inlines final code.
 
It is sorta covered in the JavaRanch Style Guide.
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!