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

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.

Related

Boolean value differs with calling println for method call and calling print ln for variable containing boolean return value of method call [duplicate]

I ran into an interesting (and very frustrating) issue with the equals() method today which caused what I thought to be a well tested class to crash and cause a bug that took me a very long time to track down.
Just for completeness, I wasn't using an IDE or debugger - just good old fashioned text editor and System.out's. Time was very limited and it was a school project.
Anyhow -
I was developing a basic shopping cart which could contain an ArrayList of Book objects. In order to implement the addBook(), removeBook(), and hasBook() methods of the Cart, I wanted to check if the Book already existed in the Cart. So off I go -
public boolean equals(Book b) {
... // More code here - null checks
if (b.getID() == this.getID()) return true;
else return false;
}
All works fine in testing. I create 6 objects and fill them with data. Do many adds, removes, has() operations on the Cart and everything works fine. I read that you can either have equals(TYPE var) or equals(Object o) { (CAST) var } but assumed that since it was working, it didn't matter too much.
Then I ran into a problem - I needed to create a Book object with only the ID in it from within the Book class. No other data would be entered into it. Basically the following:
public boolean hasBook(int i) {
Book b = new Book(i);
return hasBook(b);
}
public boolean hasBook(Book b) {
// .. more code here
return this.books.contains(b);
}
All of a sudden, the equals(Book b) method no longer works. This took a VERY long time to track down without a good debugger and assuming the Cart class was properly tested and correct. After swaapping the equals() method to the following:
public boolean equals(Object o) {
Book b = (Book) o;
... // The rest goes here
}
Everything began to work again. Is there a reason the method decided not to take the Book parameter even though it clearly was a Book object? The only difference seemed to be it was instantiated from within the same class, and only filled with one data member. I'm very very confused. Please, shed some light?
In Java, the equals() method that is inherited from Object is:
public boolean equals(Object other);
In other words, the parameter must be of type Object. This is called overriding; your method public boolean equals(Book other) does what is called overloading to the equals() method.
The ArrayList uses overridden equals() methods to compare contents (e.g. for its contains() and equals() methods), not overloaded ones. In most of your code, calling the one that didn't properly override Object's equals was fine, but not compatible with ArrayList.
So, not overriding the method correctly can cause problems.
I override equals the following everytime:
#Override
public boolean equals(Object other){
if (other == null) return false;
if (other == this) return true;
if (!(other instanceof MyClass)) return false;
MyClass otherMyClass = (MyClass)other;
...test other properties here...
}
The use of the #Override annotation can help a ton with silly mistakes.
Use it whenever you think you are overriding a super class' or interface's method. That way, if you do it the wrong way, you will get a compile error.
If you use eclipse just go to the top menu
Source --> Generate equals() and
hashCode()
Slightly off-topic to your question, but it's probably worth mentioning anyway:
Commons Lang has got some excellent methods you can use in overriding equals and hashcode. Check out EqualsBuilder.reflectionEquals(...) and HashCodeBuilder.reflectionHashCode(...). Saved me plenty of headache in the past - although of course if you just want to do "equals" on ID it may not fit your circumstances.
I also agree that you should use the #Override annotation whenever you're overriding equals (or any other method).
Another fast solution that saves boilerplate code is Lombok EqualsAndHashCode annotation. It is easy, elegant and customizable. And does not depends on the IDE. For example;
import lombok.EqualsAndHashCode;
#EqualsAndHashCode(of={"errorNumber","messageCode"}) // Will only use this fields to generate equals.
public class ErrorMessage{
private long errorNumber;
private int numberOfParameters;
private Level loggingLevel;
private String messageCode;
See the options avaliable to customize which fields to use in the equals. Lombok is avalaible in maven. Just add it with provided scope:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.14.8</version>
<scope>provided</scope>
</dependency>
in Android Studio is
alt + insert ---> equals and hashCode
Example:
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Proveedor proveedor = (Proveedor) o;
return getId() == proveedor.getId();
}
#Override
public int hashCode() {
return getId();
}
Consider:
Object obj = new Book();
obj.equals("hi");
// Oh noes! What happens now? Can't call it with a String that isn't a Book...
the instanceOf statement is often used in implementation of equals.
This is a popular pitfall !
The problem is that using instanceOf violates the rule of symmetry:
(object1.equals(object2) == true) if and only if (object2.equals(object1))
if the first equals is true, and object2 is an instance of a subclass of
the class where obj1 belongs to, then the second equals will return false!
if the regarded class where ob1 belongs to is declared as final, then this
problem can not arise, but in general, you should test as follows:
this.getClass() != otherObject.getClass(); if not, return false, otherwise test
the fields to compare for equality!
recordId is property of the object
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Nai_record other = (Nai_record) obj;
if (recordId == null) {
if (other.recordId != null)
return false;
} else if (!recordId.equals(other.recordId))
return false;
return true;
}

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

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;
}

Object comparison

Hello I am trying to write a Java comparison which takes the current object called MovieCard and compares it with another MovieCard passed as a parameter. code below
boolean sameName(MovieCard movieCard) {
if(this.MovieCard.equals(movieCard))
return true;
}
error cannot find symbol - variable MovieCard
The check is listed in my class before the MovieCard constructor if this makes any difference. Also I'm in a beginner programming class so we cannot use Comparator and it needs to contain this.
boolean sameName(MovieCard movieCard) {
return this.equals(movieCard);
}
MovieCard is a class, not a variable. And that's what the compiler is telling you.
if(this.MovieCard.equals(movieCard))
↑
You probably have to write only this.
Also note that there exists a path that doesn't return anything, this won't compile.
You should do:
boolean sameName(MovieCard movieCard) {
return this.equals(movieCard);
}
Or
boolean sameName(MovieCard movieCard) {
if(this.equals(movieCard)) {
return true;
}
return false;
}
use like
boolean sameName(MovieCard movieCard) {
if(this.equals(movieCard))
return true;
}
But it seems your method is checking whether two object has same name. if so then you can simply use
boolean sameName(MovieCard movieCard) {
if(this.name.equals(movieCard.name))
return true;
}
assuming name is a member variable(string)
If this method is present in your MovieCard class, then just this would suffice.
boolean sameName(MovieCard movieCard) {
if(this.equals(movieCard)) {// this is enough. this.MovieCard is wrong, as there is no variable by that name.
return true;
}
return false; // Since return true is a conditional return, but the method needs to return in any case.
}
And a shortened version of your method would be
boolean sameName(MovieCard movieCard) {
return this.equals(movieCard); // Returns the result of the comparison.
}
If looking at the method name, sameName(), it looks like you want to compare some name field in the class, instead of the objects as such.
boolean sameName(MovieCard movieCard) {
return this.getName().equals(movieCard.getName()); // Returns the result of the name comparison.
// But there is a possible NPE in the above statement.
}
You have to write
boolean sameName(MovieCard movieCard) {
if (this.equals(movieCard)){
return true;
} else{
return false;
}
}
Because this.MovieCard points to a variable in your Moviecard class.
Besides your syntax error, Your equals implementation does nothing other that default implementation
Remember if (this.equals(movieCard)){ This is only true when the movieCard and this points to the same reference.
Because,
Default implementation of equals() class provided by java.lang.Object compares memory location and only return true if two reference variable are pointing to same memory location i.e. essentially they are same object.
I guess you need to override the equals method in your MovieCard class, Such that comparing the fields of MovieCard's are same or not.
Prefers to read, How to essentially Ovveride equals.
You are moving in a wrong direction, Please see the example in the above link and then implement equals
in such a way that If all the fields in MovieCard equals to the passed Object's fields. That would be the implementation you asked for.
In general an example for comparing two Tiger Object's for equals like this
 
#Override
  public boolean equals(Object object) {
    boolean result = false;
    if (object == null || object.getClass() != getClass()) {
      result = false;
    } else {
      Tiger tiger = (Tiger) object;
      if (this.color == tiger.getColor() //checking color and pattern same
          && this.stripePattern == tiger.getStripePattern()) {
        result = true;
      }
    }
    return result;
  }
all the answers given above answers your question so i would like to add this too.
keep in mind that the equals method called belongs to the object method. so it would be wise for you to override the equals method to fit the needs of your program. and when you override equals make sure you override the hashcode method too..

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..

Java LinkedList Iterators: Why are they returning only Objects?

Here, I'll just post my code:
int len = InternalList.size();
ListIterator<E> forward = InternalList.listIterator( 0 );
ListIterator<E> backward = InternalList.listIterator( len );
while( forward.hasNext() && backward.hasPrevious() )
{
E next = forward.next();
E prev = backward.previous();
// When the object references are the same, we expect to be at the
// center of the list (for odd-numbered lists?); we're done
if( next == prev )
return true;
// Otherwise, if the object values aren't the same, we're not a
// palindrome
if( !((E)next).equals( prev ) )
return false;
}
And here's the internal list:
private LinkedList<E> InternalList;
So basically my problem is the last if statement only checks Object's equals() method; not the E's equals(). If forcibly casting it doesn't work, what does?
The runtime types of the elements returned from the iterators are not (and indeed cannot be) changed. They're assigned to fields of type E, which may well be erased to Object at runtime (depending on the generic bounds) but this won't affect the objects themselves.
When equals() is invoked, it's a non-static method and so is invoked on whatever the class of the next object happens to be. If this class doesn't have an overridden equals method then sure, the default Object.equals will be used. However, if this object's class directly or indirectly overrides equals, the most specific override will be used.
In other words, this code should be fine (and the cast is completely unnecessary).
I suggest that you double-check you've overridden equals correctly in the class in question. I would guess that you've implemented it as something like:
public class MyFoo {
...
public boolean equals(MyFoo other) {
...
}
}
whereas the argument must be of type Object, otherwise you're just overloading the equals method instead of overriding it. If you're using Java 6, you can add the #Override annotation to your method, which will catch this sort of error.
The correct implementation of equals(Object) will be chosen at runtime, due to runtime polymorphism. Why do you think that's not the case?
Actually, you might have made a common mistake and implemented equals(ASpecificType) instead of equals(Object): you want to override the equals(Object) method from java.lang.Object. Specifying a different parameter type means you no longer override that method.
A common equals() implementation for ASpecificType could start like this:
public boolean equals(Object o) {
if (this==o) {
return true;
} else if (o==null || o.getClass() != getClass()) {
return false;
}
ASpecificType other = (ASpecificType) other;
// insert specific comparison here
return result;
}
The casting casts E to E so it doesn't do anything.
equals should work without casting.
As you posted in the comment, next == prev will not work for even-numbered lists.
Concerning how to implement equals:
public boolean equals(Object o) {
if(this == o) { return true; }
if(o == null) { return false; }
if(o instanceof [ClassOfThis]) {
o = (Type)o;
// compare here.
} else {
return false;
}
}

Categories

Resources