I'm trying to write an equals method for objects that compares their fields and return true if they're equal.
private int x, y, direction;
private Color color;
public boolean equals(Ghost other){
if (this.x == other.x && this.y == other.y &&
this.direction == other.direction && this.color == other.color)
return true;
else
return false;
}
What could be wrong with this?
Since color appears to be a Color, that's a class, and therefore a reference type, which means you need to use equals() to compare the colors.
if (/* ... && */ this.color.equals(other.color)) {
As noted in the comments, using == to compare reference types is really comparing memory addresses in Java. It'll only return true if they both refer to the same object in memory.
akf points out that you need to use the base Object class for your parameter, otherwise you're not overriding Object.equals(), but actually overloading it, i.e. providing a different way of calling the same-named method. If you happen to pass an object of a totally different class by accident, unexpected behavior might occur (although then again if they are of different classes it will return false correctly anyway).
#Override
public boolean equals(Object obj) {
if (!(obj instanceof Ghost))
return false;
// Cast Object to Ghost so the comparison below will work
Ghost other = (Ghost) obj;
return this.x == other.x
&& this.y == other.y
&& this.direction == other.direction
&& this.color.equals(other.color);
}
In principle, this looks fine.
Note however that you are comparing using ==. For primitives, this is no problem, but for objects it will check for the same instance, not the same value. This may or may not be what you want. If you are comparing e.g. java.lang.Strings, you'd want to use equals instead (and check for null).
If you are comparing object variables instead of primitive types, you should be using a this.color.equals(other.color) comparison instead.
In your case, it also depends on how you created the Color objects. if you used the static instances (such as Color.BLUE), then actually, it shouldn't matter. If you created the Color object from rgb values, it definitely matters. Either way, it is best to get used to using .equals() for object variables.
One thing to consider is that you are not overriding the equals method from Object, as you are changing the param type. You might find this method will not be used in all cases as you might expect. Instead of:
public boolean equals(Ghost other){
you should have:
public boolean equals(Object other){
and then internally test whether the other param is an instanceof Ghost and cast as necessry.
Related
I researched this question and the answers I got do not satisfy me as they don't explain these things deeply enough. So, it is known that for HashSet with a parametrized custom class it is necessary to override hashCode and equals in order to forbid duplicates. But in practice when I tried to understand how this really works I didn't quite get it.
I have a class:
static class Ball {
String color;
public Ball(String color) {
this.color = color;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Ball ball = (Ball) o;
return Objects.equals(color, ball.color);
}
#Override
public int hashCode() {
return Objects.hash(color);
}
}
In equals method, it is all clear. If two 'variables' are pointing to the same object in memory, then they are equal; if an o is null or they are not of the same class - they are not equal.
The last line of equals is what's concerning me.
When I go to the Objects.equals :
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
It says once again if two 'variables' refer the same object, then they are equal or if the first object is not null and it is equal to the second one. This equals is Object.equals which will return true if only these two objects aka 'variables' are pointing to the same object in memory.
So, how does this really works? Been looking for a clear answer, but as I said, what I've got so far does not satisfy me at all.
In your class, you explained it very well.
The part that you are missing is that at some point, your code will delegate to the equals and hashCode on the color attribute, which is implemented by the java.lang.String class.
See e.g. https://github.com/openjdk-mirror/jdk7u-jdk/blob/f4d80957e89a19a29bb9f9807d2a28351ed7f7df/src/share/classes/java/lang/String.java#L1013 and https://github.com/openjdk-mirror/jdk7u-jdk/blob/f4d80957e89a19a29bb9f9807d2a28351ed7f7df/src/share/classes/java/lang/String.java#L1494
tl;dr
return Objects.equals(color, ball.color);
You are passing two String objects here, not Ball objects.
Your override of Object#equals compares Ball objects, while the line above compares the String objects referenced by the color field of Ball.
Details
First, let’s clarify the terminology.
This equals is Object.equals which will return true if only these two objects aka 'variables' are pointing to the same object in memory.
In your equals method, both this and Object o are reference variables, not objects. Either variable can hold no reference at all (null) or either variable can hold a reference (pointer) to an object living elsewhere in memory.
Next we can examine your code.
Three Phases of equals
The logic of your equals is three phased:
Identity check
Null check
Content comparison
The first phase performs an identity check. We look to see if both reference variables refer to the same object, the same chunk of memory. If so, there is no need for further consideration: An object is always equal to itself. So return true, job done.
The second phase performs a null check. If either of the two objects being compared is null, we report false, meaning “not equal”. To perform the null check, we skip this. The this reference variable cannot be null by definition. We move on to the Object o. If null, report false, job done.
The third phase compares content. We examine the content of the object referenced by this. And we examine the content of the object referenced by Object o. We know we have two separate objects (two separate chunks of memory) because at this point in the code we got past the identity check.
In your case with the Ball class, you chose to compare the one and only piece of state, the member field color. That member field holds a reference to a String object. So, after casting o to Ball ball, we compare the string from this.color to the string from ball.color.
This seems to be the sticking point in your understanding. 👉 The casting is crucial here. After successfully casting from Object o to Ball ball, the Java Virtual Machine at runtime knows that the object in question is indeed a Ball (or a subclass of Ball). As a Ball, we have access to its color field.
In the call to Objects.equals(color, ball.color) we are comparing two String objects, not two Ball objects. You may find clarity in expanding that code.
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Ball ball = (Ball) o;
String thisColor = this.color ;
String thatColor = ball.color ;
boolean colorsAreTheSame = thisColor.equals( thatColor );
return colorsAreTheSame ;
}
You said:
When I go to the Objects.equals :
Your code is passing two String objects to Objects.equal, not two Ball objects.
By the way, do not confuse Objects.equals with Object#equals.
This first is a static method on the utility class Objects — notice the plural s.
The second is an instance method defined on the ultimate superclass Object — note the singular, with no s on the end. The second is inherited by Ball, but then overridden by the Ball class’ own implementation. So the implementation provided by Object#equals is never used in your scenario.
You said:
This equals is Object.equals which will return true if only these two objects aka 'variables' are pointing to the same object in memory.
You cut it short there. The Object.equals method performs the same three phased logic as your equals method: firstly identity check, secondly null check, and thirdly whatever is implemented in the equals method of the two objects.
And most importantly, you are passing two String objects to Objects.equals, whereas your override of equals compares two Ball objects.
By the way, if the purpose of your class is to communicate date transparently and immutably, you can more briefly define the class as a record in Java 16+.
In a record, you merely declare the type and name of each member field. The compiler implicitly creates the constructor, getters, equals & hashCode, and toString.
The default for this methods is to utilize each and every member field. You can override equals & hashCode you want to consider a subset of the member fields.
Here is your entire Ball class when written as a record.
record Ball ( String color ) {}
Usage:
Ball redBall = new Ball( "red" ) ;
Ball blueBall = new Ball( "blue" ) ;
boolean sameBall = redBall.equals( blueBall ) ; // false
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.
I am writing the image processing program, and I have a problem with a list.
I have a list with Points.
class Point1{
private int x;
private int y;
Point1(int x,int y)
{
this.x = x;
this.y = y;
}
int getX(){ return this.x; }
int getY() {return this.y; }
}
ArrayList<Point1> list = new ArrayList();
And now, I create new Point, for example new Point(4,3);
I want to check if there is a point in my list which has the same coordinates. The problem is that
list.contains(Object a)
is checking if if on my list there is a particular object. It will work if I put
Point1 first = new Point(1,1);
list.add(first);
list.contains(first) // and this is true
but:
Point second = new Point(2,2);
list.add(second);
list.contains(new Point(2,2)); <- false
how can I check it?
You need to override equals on your Point class. Your Point class must be responsible for determining whether another Point object is "equal" to the current object. An ArrayList will call equals to determine if the object passed in to contains is "equal" to any item in the list.
If you don't override equals, then Point will inherit equals from Object, which will simply see if it's the same exact object. That's why your first code "works", because you are re-using first. It also explains why your second code doesn't "work", because you used a different object.
Also, if you override equals, it's best to override hashCode also (and vice versa).
List determine whether two objects are equal by using equals method.
If your class won't override public boolean equals(Object o){..} method, it will will inherit it from closest supertype which provides that implementation.
If your class doesn't extend any other class, it will means it implicitly extends Object class. So it will inherit equals implementation from it. Problem is that this implementation looks like:
public boolean equals(Object obj) {
return (this == obj);
}
so it uses reference equality operator ==. This means that equals will only return true if object will be compared with itself (references to other objects will always be different than reference to this object).
In your case your equals method to return true also for other instances of Point1 if their state (value of x and y) is equal.
#Override
public boolean equals(Object other) {
if(this == other)
return true;
if(!other instanceof Point1)
return false;
Point1 otherPoint= (Point1)other;
if(this.x == otherPoint.getX() && this.y == otherPoint.getY())
return true;
return false;
}
And you can get the result you want:
BTW while overriding equals method we should also override hashcode method. See Why do I need to override the equals and hashCode methods in Java?
Let's say I have two objects, A and B where..
Object A=new Object();
Object B=A;
These objects by default each have two ints: int X and int Y. First, in both A and B,
(X == 0) && (Y == 0)
So, you would say those two are equal, as would Java. Now, let's say we change A.X so that A.X=2. Now, A and B are no longer equal since
A.X==2
..but..
B.X==0
Java, however, still says they are equal.
(A.equals(B)) == true
(B.equals(A)) == true
So, how do you get around that?
By doing this Object B=A;, you are not creating a new object, but B is pointing to A only. So its only one object.
So when you change A.X = 2, B.X is also 2 at its referring the same variable and hence equal.
You may verify this by printing B.X value.
I think everyone (except Mr Singh) is missing a point here:
Object A=new Object(); // Implication is that this is really a user-defined class
Object B=A;
You only have one object here. If you make a change to object A the same change will appear in object B, since they are the exact same object.
You need to override the .equals method of your class that contains the variables. Take a look at this question: How to override equals method in java
If Object is really your own class and not java.lang.Object (if it were then it would have those variables x and y) then it's really a bad class name choice.
Your class must override the .equals method as:
#Override
public boolean equals(Object obj) {
if(this == obj) return true;
if(!(obj instanceof MyObject)) return false;
MyObject other = (MyObject) obj;
return other.x == this.x && other.y == this.y;
}
this question is specifically about the performance and to some extent brevity of the various implementation alternatives.
I refreshed myself with this article on implementing equality right. My question particularly corresponds to canEqual (to ensure equivalence relation).
instead of overloading canEquals method to use instanceOf in every class in the hierarchy( instance of paramenter is a compile time class ). Why not use isAssignableFrom ( which is resolved dynamically ) in only the top level class. Makes for much concise code and you dont have to overload a third method.
While, this alternative works. Are there any performance considerations that I need to be aware of?
enum Color {
RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET;
}
class Point {
int x;
int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
#Override public boolean equals(Object other) {
boolean result = false;
if (other instanceof Point) {
Point that = (Point) other;
//Option 1
//result = (that.canEqual(this) && this.getX() == that.getX() && this.getY() == that.getY());
//Option 2
//result = (that.getClass().isAssignableFrom(this.getClass()) && this.getX() == that.getX() && this.getY() == that.getY());
//Option 3
//result = (getClass() == that.getClass() && this.getX() == that.getX() && this.getY() == that.getY());
}
return result;
}
#Override public int hashCode() {
return (41 * (41 + x) + y);
}
public boolean canEqual(Object other) { return (other instanceof Point); }
}
public class ColoredPoint extends Point{
Color color;
public ColoredPoint(int x, int y, Color color) {
super(x, y);
this.color = color;
}
#Override public boolean equals(Object other) {
boolean result = false;
if (other instanceof ColoredPoint) {
ColoredPoint that = (ColoredPoint) other;
result = (this.color.equals(that.color) && super.equals(that));
}
return result;
}
#Override public int hashCode() {
return (41 * super.hashCode() + color.hashCode());
}
#Override public boolean canEqual(Object other) { return (other instanceof ColoredPoint); }
public static void main(String[] args) {
Object p = new Point(1, 2);
Object cp = new ColoredPoint(1, 2, Color.INDIGO);
Point pAnon = new Point(1, 1) {
#Override public int getY() {
return 2;
}
};
Set<Point> coll = new java.util.HashSet<Point>();
coll.add((Point)p);
System.out.println(coll.contains(p)); // prints true
System.out.println(coll.contains(cp)); // prints false
System.out.println(coll.contains(pAnon)); // prints true
}
}
Update: Actually, your method is not technically valid like I first thought, because it breaks the symmetry contract of equals for subclasses that don't override equals:
Point p = new Point(1, 2);
Point pAnon = new Point(1, 1) {
#Override public int getY() {
return 2;
}
};
System.out.println(p.equals(pAnon)); // prints false
System.out.println(pAnon.equals(p)); // prints true
The reason is that p.getClass().isAssignableFrom(pAnon.getClass()) is true while the inverse, pAnon.getClass().isAssignableFrom(p.getClass()) is false.
If you are not convinced by this, try actually running your code and compare it to the version in the article: you will notice that it prints true, false, false, instead of true, false, true like the example in the article.
unless you want to allow comparing classes of different types, the easiest, safest, most concise and probably most efficient impl is:
(getClass() == that.getClass())
All the answers given so far don't answer the question - but point out the equals() contract. Equality must be an equivalence relation (transitive, symmetric, reflexive) and equal objects must have the same hash code. That extends perfectly fine to subclasses - provided subclasses don't themselves override equals() or hashCode(). So you have two choices - you either inherit equals() from Point (so ColoredPoint instances are equal if they have the same coordinates, even if they have a different color), or you override equals() (and now must make sure a Point and a ColoredPoint are never equal).
If you need to perform a pointwise comparison, then don't use equals() - write a method pointwiseEquals() instead.
Whatever you choose to do, you still have to perform the class check in equals().
getClass() == that.getClass()
is clearly the best performer, but it does break if you expect to be able to equality test subclasses that don't themselves override equals() (and in practice, the only way you can guarantee that is to make the class or the equality methods final and not allow any subclasses to override at all). If it's a choice between instanceOf and isAssignableFrom, there's no practical difference, they both in fact perform the same run-time test (the only difference is, instanceOf can perform a compile-time sanity check, but in this case, it can't know anything when the input is just Object). In both cases, the runtime check is identical - check for the target class in the object's listed interfaces (which doesn't apply here, since we're not checking for an interface), or walk up the class hierarchy until we either find the listed class or get to the root.
See my answer for What is the difference between equality and equivalence?.
You can't equate two objects from different classes because it breaks symmetry.
Edit:
It comes down to whether x in the following:
if (other instanceof Point) {
Point that = (Point) other;
boolean x = that.getClass().isAssignableFrom(this.getClass());
}
has the same power as getClass() == that.getClass().
According to #waxwing's answer it doesn't.
Even if it were correct, I don't see any performance benefit here by calling that.getClass().isAssignableFrom.
Here's my Second Answer to the clarified question
Consider when we call Point.equals(ColoredPoint cp);
Point.equals() first checks for
if (other instanceof Point)...
Which passes. Out of the three options presented, all three of them check that the other object, in this case a ColoredPoint, satisfies some more test. The options are:
will only be true if Point is an instanceof ColoredPoint, which is never
will only be true if ColoredPoint is assignable from Point, which is never
will never be true.
From a performance (and design) perspective, there was no value in checking for other instanceof Point, because the actual behavior OP wants (which he has been unable to express) is that for his particular use case, equality between these Objects means they must be the same class.
Therefore, for both performance and design, just use
this.getClass() == that.getClass()
as was suggested by #jthalborn
When a later coder sees instanceof or isAssignableFrom in your code, he will think that subclasses are allowed to equal the base class, which is completely misleading.
I think you solution will fail because it isn't transitive OOPS, symmetric. See The chapter from Effective Java
Point p = new Point(2,3);
ColoredPoint cp = new ColoredPoint(2,3, Color.WHITE);
I believe (haven't run your code) that
p.equals(cp) is true
but
cp.equals(p) is false
Though I don't fully understand your code - it refers to canEquals() which was commented out. The short answer is that you either have to ignore color for equality, or you have to do what #jthalborn suggested.
OK we here we have the example from Effective Java (i have the 2nd Edition 2008).
The example is in ITEM 8: OBEY THE GENERAL CONTRACT WHEN OVERRIDING EQUALS starting from page 37 (I write this in case you want to check).
class ColoredPoint extends Point{} and there are 2 attepts in demostrating why instanceof is BAD. The first attempt was
// Broken - violates symmetry!
#Override public boolean equals(Object o) {
if (!(o instanceof ColorPoint))
return false;
return super.equals(o) && ((ColorPoint) o).color == color;
}
and the second was
// Broken - violates transitivity!
#Override public boolean equals(Object o) {
if (!(o instanceof Point))
return false;
// If o is a normal Point, do a color-blind comparison
if (!(o instanceof ColorPoint))
return o.equals(this);
// o is a ColorPoint; do a full comparison
return super.equals(o) && ((ColorPoint)o).color == color;
}
First of all the second IF will never be reached. If 'o' is not a Point which is a superclass to ColorPoint how might it happen a non-Point to be a ColorPoint ??????
So the second attempt from the beginning is wrong ! Where the only chance for a TRUE comparison is super.equals(o) && ((ColorPoint)o).color == color;, which is not enough !!
a solution here would be:
if (super.equals(o)) return true;
if (!(o instanceof ColorPoint))
if ((o instanceof Point)) return this.equals(o);
else return false;
return (color ==((ColorPoint)o).color && this.equals(o));
obj.getClass() is used for very specific equals(), But your implementations depends of your scope. How do you define that two objects are equal or not? Implement it and it will work accordingly.