Running equality in Java (instanceOf vs isAssignableFrom)

this question specifically addresses performance and, to some extent, the brevity of various implementation options.

I updated myself in this article on the realization of the right to equality. My question, in particular, matches canEqual (to provide an equivalence relation).

instead of overloading the canEquals method, use instanceOf in each hierarchy class (the paramenter instance is a compile-time class). Why not use isAssignableFrom (which is resolved dynamically) only in the top-level class. It does for very compressed code, and you do not need to overload the third method.

So far this option is working. Are there any performance considerations I need to know about?

 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 } } 
+4
source share
7 answers

Refresh . Actually, your method is not technically correct, as I thought before, because it breaks the equals symmetry contract for subclasses that do not 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 because p.getClass().isAssignableFrom(pAnon.getClass()) is true , while the opposite, pAnon.getClass().isAssignableFrom(p.getClass()) is false .

If you are not sure about this, try to execute your code and compare it with the version in the article: you will notice that it prints true, false, false instead of true, false, true , as an example in the article.

+4
source

if you do not want to compare classes of different types, the simplest, safest, most concise and probably the most efficient option:

 (getClass() == that.getClass()) 
+3
source

See my answer for What is the difference between equality and equivalence? .

You cannot equate two objects from different classes because it breaks symmetry.

Edit

Is x allowed 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, the answer is no.

Even if it was correct, I see no performance benefit by calling that.getClass().isAssignableFrom .

+1
source

All the answers given so far do not answer the question - but specify the equals() contract. Equality should be an equivalence relation (transitive, symmetric, reflexive), and equal objects should have the same hash code. This is great for subclasses - provided that the subclasses themselves do not override equals() or hashCode() . So you have two options: 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 redefine equals() (and now you need to make sure Point and a ColoredPoint never equal).

If you need to perform point-by-point comparisons, then do not use equals() - instead write the pointwiseEquals() method.

Whatever you choose, you still need to check the class in equals() .

 getClass() == that.getClass() 

by far the best performer, but it breaks if you expect you can use subclasses of equality checking that do not override equals() themselves (and in practice this is the only way to ensure that you have to make the class or methods of equality final and not allow subclasses at all override at all). If this is a choice between instanceOf and isAssignableFrom , there is no practical difference, they both actually perform the same test at runtime (the only difference is that instanceOf can perform a health check at compile time, but in this case, it doesn’t knows when the input is only Object ). In both cases, the runtime check is identical - check the target class in the interfaces listed in the object (which does not apply here, because we do not check the interface) or go to the class hierarchy until we find or go to the root directory.

+1
source

Here is my second answer to the clarified question

Consider when we call Point.equals(ColoredPoint cp);

Point.equals () first checks

 if (other instanceof Point)... 

What goes through. Of the three options presented, all three verify that another object, in this case ColoredPoint, satisfies yet another test. Possible options:

  • will be true if Point is an instance of ColoredPoint that never
  • will only be true if ColoredPoint is assigned from Point, which never
  • will never be true.

From a performance (and design) point of view, there was no value when checking for other instanceof Point , since the actual behavior of the OP he wants (which he could not express) is that for his particular use case, the equality between these objects means that they must to be of the same class.

So for performance and design just use

  this.getClass() == that.getClass() 

as suggested by @jthalborn

When a later coder sees instanceof or isAssignableFrom in your code, it will think that subclasses are allowed to equal the base class, which is completely misleading.

+1
source

I think the solution will fail because it is not transitive OOPS symmetric. See the chapter "Effective Java"

 Point p = new Point(2,3); ColoredPoint cp = new ColoredPoint(2,3, Color.WHITE); 

I believe (did not run your code) that

p.equals (cp) is true

but

cp.equals (p) is false

Although I do not quite understand your code, it refers to canEquals (), which was commented out. The short answer is that you either have to ignore the color for equality, or you need to do what @jthalborn suggested.

0
source

OK, here we have an example from Effective Java (I have the second edition of 2008). An example is in ITEM 8: OBEY THE GENERAL CONTRACT WHEN OVERRIDING EQUALS , starting on page 37 (I write this if you want to check).

class ColoredPoint extends Point{} and there are 2 attepts in the demo why instanceof is BAD. 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, a second IF will never be achieved. If the "o" is not a point that is a superclass for ColorPoint, how can this happen, the point should not be ColorPoint ??????

So, the second attempt is wrong from the very beginning! Where the only chance for a TRUE comparison is super.equals(o) && ((ColorPoint)o).color == color; what is not enough! the solution here would be as follows:

 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 depend on your area. How do you determine if two objects are equal or not? Deploy it and it will work accordingly.

0
source

Source: https://habr.com/ru/post/1392131/


All Articles