Equality with Java generics: the subclass equals isn't called [duplicate] - java

This question already has answers here:
Overriding the java equals() method - not working?
(8 answers)
Closed 9 years ago.
I've got a Node<T> with an equals() method:
public boolean equals(Node<T> other) {
if (this == other)
return true;
if (other == null)
return false;
if (!obj.getClass().equals(other.getObject().getClass()))
return false;
return obj.equals(other.getObject());
}
I only care if the object held in my node is equal to the object held in the other node (because two equal objects can be held in different positions in my list).
The object I'm holding is a Token. The Token.equals() method works while my Node.equals() method does not:
public class TokenEqualityTest {
public static void main(String[] args) {
Token t = new Token(0);
Token q = new Token(0);
System.out.println("t.equals(q): " + t.equals(q));
Node<Token> tnode = new Node<Token>(null, null, t);
Node<Token> qnode = new Node<Token>(null, null, q);
System.out.println("tnode.equals(qnode): " + tnode.equals(qnode));
}
}
which prints:
t.equals(q): true
tnode.equals(qnode): false
If I put a breakpoint at Token.equals() and run the eclipse debugger, my code stops once (at t.equals(q)). This indicates that Node<Token>.equals() does not call Token.equals, and I have verified that the debugger does step through the line return obj.equals(other.getObject());.
Why doesn't my Node.equals ever call Token.equals when I've declared a Node<Token>?

Your equals method should have the following signature:
public boolean equals(Object obj)
And when you override equals method, you should override hashCode method, too. This is contract that all object should follow.
For prevent this kind of mistake, it would be better to add #Override annotation.
Or you can use lombok to simplify defining equals and hashCode methods by #EqualsAndHashCode annotation.

My psychic debugging skills tell me that you didn't override the base equals() method in Token, just like you didn't in Node.
You need to declare it as equals(Object).
Add the #Override annotation to catch that.

The equals method must take an Object type as a parameter in order to override the parent class equals method. It should also return a primitive boolean type.

You need to override equals method this way:
#Override
public boolean equals(Object object) {
// add your logic here
}
Also you need to override hashCode method. This is the contract:
#Override
public int hashCode() {
return hashCode;
}

Related

Java equal method explanation on the following code

I have been reading a book called Thinking in Java on Java(I come from C background). I came across the following two set of codes
public class EqualsMethod {
public static void main(String[] args) {
Integer n1 = new Integer(47);
Integer n2 = new Integer(47);
System.out.println(n1.equals(n2));
}
}
//Output: true
I understand that this equal method is comparing the reference. But n1 and n2 are two object residing in ´two different "bubble" in the heap. So how come they are equal?
Another example code is
class Value {
int i;
}
public class EqualsMethod2 {
public static void main(String[] args) {
Value v1 = new Value();
Value v2 = new Value();
v1.i = v2.i = 100;
System.out.println(v1.equals(v2));
}
} /* Output:false
}
Why does this give false? Your in depth answer would be much anticipated. Thank you.
The behavior of equals in your custom classes is entirely up to you. If you override it, you decide when two objects of your class are considered equal to each other. If you don't override it, you get the default implementation of Object class, which checks if both references refer to the same object (i.e. checks if v1==v2 in your example, which is false).
Root of the issue :
You have not overrriden eqauals and hashCode and then JVM assigns a new hashCode to any object you create in the case of Value class
=================
Solution :
You will need to define the criteria on which the identities of the value object is measured i.e do the following
1) Override the equals method and specify that the equality is checked over the value of the instance variable i
2) Override Hashcode and use instance variable i for the hashCode comparison
== is used in the equals method in the object class to avoid unnecessary calculation if the two refrences point to the same object and if not go ahead with the calculation and comparisons
public boolean equals(Object anObject)
{
if (this == anObject) {
return true;
}
else{
// Do the calculation here to check the identity check
}
I understand that this equal method is comparing the reference.
Wrong. In the Object class, this method contains a referential comparison, but Integer has it's own implementation, which overrides the one provided by Object.
It compares the values of the two Integers, not their references.
Integer is valuable type. So comparing to Integer variables performing by comparing their values. Which are equal in your particular case.
Comparing two objects (reference type) performing by comparing the references, which are not equal.
You could write your own comparison logic by overloading the equals() method in your class.
Integer has the method equals() that compare the value, and your Value class doesn't. It makes the Value class with equals compare the "pointer", and they're different.
If you override the method equals in your class Value comparing the attribute i from the class, it would return true.
For example
public boolean equals(Object o){
return (this.i == ((Value) o).i) ? true : false;
}
Equals method in all Wrapper classes is overridden by default in java. That's is why first snippet works.
For your own classes, you have to provide an implementation of equals method.
By default the equal method in Java check if the two Object references are the same. You can #Override the method, and do what you want. So it is normal that you get False, because the two Object are different.
So how come they are equal?
Integer is an Object. On the other side int is a simple type.
Integer's equals() method compare int inside, because it's overriding Object equals() method. int's there has the same value.
Why does this give false?
Your Value class doesn't override equal's method, so then refferences are compared, exactly like when you write v1 == v2. In this case they are different Objects so it's false.
Because you have not override equals method. If you do not override it then it will check if the reference are equal or not and return accordingly.
You can refer equals() method defined in Integer class.
System.out.println(n1.equals(n2)) // this prints true because it refers to Integer equals method.
Similarly you will have to override it for your Value class like.
class Value {
int i;
#Override
public boolean equals(Object obj) {
boolean returnValue = false;
if (obj != null && obj instanceof Value) {
Value valObj = (Value) obj;
if (this.i == valObj.i) {
returnValue = true;
}
}
return returnValue;
}
}
Now System.out.println(v1.equals(v2)); prints true.
Hi your understanding of equals and == is completely wrong or opposite to what it actually is.
equals() method also checks for reference as == does, there is no difference between both of them unless you override the equals method.
== check for reference equality. For better understanding see Object class source code.
public boolean equals(Object obj) {
return (this == obj);
}
Why is it working in your case? is because Integer class overrides the equals method in it.
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
n
}
Now when you use your custom class to check equality what it is doing is basically calling.
v1==v2
How it can give you true? They both have different memory locations in heap.
If still things are not clear put break points in your code and run it in debug mode.

Unable to get right values from hashmap while overriding Hashcode method [duplicate]

This question already has answers here:
How do I compare strings in Java?
(23 answers)
Closed 8 years ago.
I am executing below code after overriding hashcode method of an object (BookMe). Aim is to override hashcode of the object which i will be using as a key in my map (hashmap). But, after executing I see null values. The is no problem with the actual size of map. below is the code. If I don't override hashcode method I get correct output (i mean all three values).
`
class BookMe{
private String isbn ;
static int i = 0;
public BookMe(String isbn)
{
this.isbn = isbn;
}
public String getIsbnValue()
{
return this.isbn;
}
#Override
public boolean equals(Object o)
{
if(o instanceof BookMe && ((BookMe)o).getIsbnValue() == this.getIsbnValue())
{
return true;
}
else{
return false;
}
}
#Override
public int hashCode()
{
return this.isbn.toString().length() + (++i);
}
}
public class HashMapTest {
public static void main(String[] args) {
Map<BookMe, Integer> map = new HashMap<BookMe, Integer>();
BookMe b1 = new BookMe("Graham");
BookMe b2 = new BookMe("Graham");
BookMe b3 = new BookMe("Graham");
map.put(b1, 19);
map.put(b2, 33);
map.put(b3, 22);
System.out.println("----444444--------");
System.out.println(map.size());
Set <BookMe> set = map.keySet();
System.out.println("------*****------");
for(BookMe bk : set)
{
System.out.println("bk : "+ bk);
System.out.println(map.get(bk));
}
}
}
`
Your hashCode violates the contract of Object::hashCode. It should return the same value for the same object.
From the Javadoc :
* <li>Whenever it is invoked on the same object more than once during
* an execution of a Java application, the <tt>hashCode</tt> method
* must consistently return the same integer, provided no information
* used in <tt>equals</tt> comparisons on the object is modified.
* This integer need not remain consistent from one execution of an
* application to another execution of the same application.
If each call to hashCode for the same instance returns a different value, you can't expect your HashMap to locate your keys.
If you want a hashCode based on the ISBN, just return the hashCode of the ISBN :
#Override
public int hashCode()
{
return this.isbn.hashCode();
}
You should also fix your equals method :
public boolean equals(Object o)
{
if(o instanceof BookMe && ((BookMe)o).getIsbnValue().equals(this.getIsbnValue()))
{
return true;
}
else{
return false;
}
}
In addition to what Eran said your hash code is ever going to return new hash code even for the same object say when you are putting the element into map and when you are iterating over the same, You should always use equals method to compare the two string.
((BookMe)o).getIsbnValue() == this.getIsbnValue()
Change it to:
((BookMe)o).getIsbnValue().equals(this.getIsbnValue())
Your hashcode method is broken and also in equals method you are comparing string by reference not by value . You can correct the equals method like this
#Override
public boolean equals(Object o) {
return o instanceof BookMe && ((BookMe) o).getIsbnValue().equals(this.getIsbnValue());
}
And to correct hashcode , you can use Objects method to generate hashcode
#Override
public int hashCode() {
return Objects.hash(isbn);
}
The general contract for overridden implementations of this method is
that they behave in a way consistent with the same object's equals()
method: that a given object must consistently report the same hash
value (unless it is changed so that the new version is no longer
considered "equal" to the old), and that two objects which equals()
says are equal must report the same hash value. There's no requirement
that hash values be consistent between different Java implementations,
or even between different execution runs of the same program, and
while two unequal objects having different hashes is very desirable,
this is not mandatory (that is, the hash function implemented need not
be a perfect hash).

Trouble in overriding the equals(...) method in Java [duplicate]

This question already has answers here:
When overriding equals in Java, why does it not work to use a parameter other than Object?
(7 answers)
Closed 9 years ago.
I have a superclass Order which has a subclass OrderBook. The equals(...) method is overridden in the OrderBook. The method implementation is as follows:-
public boolean equals(Order o)
{
if(o==null){
System.out.println("object is null.");
return false;
}
if(o==this){
System.out.println("The object is itself.");
return true;
}
if(o instanceof OrderBook)
{
OrderBook o1 = (OrderBook)o;
if(!(o1.productId.equals(productId))){
System.out.println("productId mismatch.");
return false;
}
if(!(o1.customerId.equals(customerId))){
System.out.println("customerId mismatch.");
return false;
}
if(o1.book!=book){
System.out.println("book mismatch.");
return false;
}
}
return true;
}
I am encountering an unexpected output if I give the following statement:-
Order order1 = new OrderBook("Algorithms","Kunal",false);
Order order2 = new OrderBook("Algorithms","Kunal",false);
System.out.println(order1.equals(order2));
It should display true but its displaying false. Also, if I change the parameter in the equals(...) to Object o, then the whole program runs without any problem.
My doubt is that why are we using Object when we should be using Order?
public boolean equals(Order o)
Should be
public boolean equals(Object o)
Explanation
This is because you're attempting to override a method. When you override a method, you need to match the signature exactly. The signature is made up of:
The name of the method
The type of the arguments
The number of arguments
The return type.
The type of the argument in the original method signature was Object, yet you've provided an object of type Order. Hence, the runtime will treat these are two distinct methods.
try putting this instead of your block in the books comparing part
if(!o1.book.equals(book)){
System.out.println("book mismatch.");
return false;
}
And change the signature it should be equals(Object o)
Also do not forget the #Override annotation
To override a method in Java, you must match its signature exactly. The signature of Object#equals is:
public boolean equals(Object o)
a fairly standard approach is to overriding equals is:
public boolean equals(Object o) {
if (obj == null)
return false;
if (obj == this)
return true;
if (!(obj instanceof Order))
return false;
// specific comparisons for your Order object
}
Everyone did a good job explaning how to make it work. I want to explain why your's didnt work: (pay close attention to the type of parameter in equals())
Object class has equals(Object ojb). When JVM encounters the statement "order1.equals(order2)" it says to itself: Hmmmm ... order is of type Order and references OrderBook. I need to use the overriden equals() method if OrderBook has one. Dang!! OrderBook doesnt have equals(Object) instead it has equals(Order). So Im going to use equals(Object) from Object class.
What you have is called OVERLOADING. not overriding.

Why isn't my equals method working?

I'm using an ArrayList, and at one point in the program I'm using the contains method to check if a certain item is contained in the ArrayList. The ArrayList holds objects of type CharProfile, a custom class, and it's seeing if a char is contained within it.
So it's using the equals method in the contains method, I assume. So something like CharProfile.contains(char), but it's not working.
I overrode the equals method in CharProfile:
#Override
public boolean equals(Object o) {
if (this.character == (Character)o) {
return true;
}
else {
return false;
}
}
So it should be using my equals method when CharProfile is trying to use it, right? So why won't it work?
(In terms of "not working" I'm referring to the fact that contains always returns false.)
You are comparing a reference type using ==, which is wrong. You must use equals, with proper null-checks added.
But this is just the beginning. Your main problem is that you are trying to compare a CharProfile object to a Character object. You probably need this instead:
public boolean equals(Object o) {
return o instanceof CharProfile
&& this.character.equals((CharProfile)o).character;
}
This assumes that your character field is never null. If it can be null, you need to check that before dereferencing it, as well.
You are overriding equals such that it test for equality of reference, the default behavior of the operator ==
You need to use equals(). You can also make it a oneliner and be more explicit in your cast.
#Override
public boolean equals(Object o) {
return o instanceof Character && this.character.equals(Character.class.cast(o));
}
You have to use the equals() method and DO NOT forget to override the hashCode() method as well. They go hand in hand.
Some people don't know this, but if using eclipse you can right click choose Source-> and Generate hashCode() and equals()...
But, I suggest that you learn what they're for first before using this convenience.
For example, You have CharProfile as below.
List<CharProfile> list = new ArrayList<CharProfile>();
list.add(new CharProfile('a'));
list.add(new CharProfile('b'));
list.add(new CharProfile('c'));
When list.contains('a') is does, the JVM will not call Override equals() method of CharProfile.
For more clear;
public class Data {
public boolean equals(Object o) {
System.out.println("I am data");
return false;
}
}
When list.contains(new Data()) is does, the JVM will not call Override equals() method of Data.
Now, You will get message like I am data..

equals java unexpected result

I just started learning Java and came across equals. After looking for the difference between equals and ==, I decided to practice it myself but I am not getting the expected results. here is the code:
public class Sandbox {
/**
* #param args
*
*/
private int a;
public void setAtta(int value){a=value;}
public int getAtta(){return a;}
public static void main(String[] args) {
// TODO Auto-generated method stub
Sandbox s = new Sandbox();
s.setAtta(10);
Sandbox s1 = new Sandbox();
s1.setAtta(10);
System.out.println(s==s1);//false- EXPECTED
System.out.println(s.equals(s1));//false- I thought it should be true?
}
}
Object.equals in Java is equivalent to ==, i.e. it tests reference equality. Since your Sandbox class (implicitly) extends Object, and you don't override equals, s.equals(s1) calls Object.equals.
To get the behaviour you want, add an equals method (override) to your class:
public boolean equals(Object obj) {
if(this == obj) return true;
if(!(obj instanceof Sandbox)) return false;
Sandbox that = (Sandbox)obj;
return this.a == that.a;
}
equals() method in object class, just use == comparison behind the screen. So you got it as false. So you need to override it and give your implementation as needed.
public boolean equals(Object o){
if(o instanceof SandBox){
if(((SandBox)o).a==this.a){
return true;
}
}
return false;
}
This is how it works:
equals is an Object class method that you can override.
In String class, it is already overridden.
If you want your code, to work, you have to define your own equals method code. Because, obviously, Object class's code is not valid for sandbox class.
The method signature for equals is:
public boolean equals(Object obj);
the difference between == and equals is that == checks equality in references and equals checks equality in value. tell me please, what is the value of an object? that is why the result is false. unless overridden the equals of sandbox will invoke the equals of the Object class. You should override the equals function to check equality between you custom objects.

Categories

Resources