Why this overloading example works this way?

  • Thread starter Gabriel Belingueres
  • Start date
G

Gabriel Belingueres

The problem is in

if (!other.getClass().equals(this.getClass())) {
return other.equals(this);
}

this result in an infinite recursive call when you try to compare this
object with some object of a subclass of it.

Oliver asumes that 2 objects are equal if they are both of the same
class, which is not allways correct as you trivially found.
equals(Object) is intentionally informal about the meaning of equality.
The only formal definition is that is must define an equivalence
relation on the comparable objects (reflexive, simmetric and transitive
properties on objects)

Patricia Shanahan ha escrito:
Oliver Wong wrote:
...
*: Actually, making equals "aware" of class hierarchies is slightly trickier
than that. I wanted to gloss over this in my above example, but here's a
more complete example implementation:

public class SomeClass {
final int field1;
final String field2;

/*Constructors, getters, and hashcode*/

@Override
public boolean equals(Object other) {
if (other instanceof SomeClass) {
return equals((SomeClass)other);
}
return false;
}

public boolean equals(SomeClass other) {
if (!other.getClass().equals(this.getClass())) {
return other.equals(this);
}
if (this.field1 != other.field1) {
return false;
}
if (!this.field2.equals(other.field2)) {
return false;
}
return true;
}
}

I got worried about whether this code would ever make up its mind, given
a subclass comparison. It doesn't:

public class SomeClass {
final int field1 = 0;
final String field2 = "test";

public static void main(String[] args) {
Object obj1 = new SomeClass();
Object obj2 = new SomeSubClass();
System.out.println(obj1.equals(obj2));
}


/*Constructors, getters, and hashcode*/

@Override
public boolean equals(Object other) {
if (other instanceof SomeClass) {
return equals((SomeClass)other);
}
return false;
}

public boolean equals(SomeClass other) {
if (!other.getClass().equals(this.getClass())) {
return other.equals(this);
}
if (this.field1 != other.field1) {
return false;
}
if (!this.field2.equals(other.field2)) {
return false;
}
return true;
}

private static class SomeSubClass extends SomeClass{
}
}

This program results in a StackOverflowError. obj1 passes the comparison
to obj2, which passes it back to obj1... What am I doing wrong? Are
subclasses of SomeClass required to override equals with specific rules?

Patricia
 
O

Oliver Wong

Patricia Shanahan said:
Oliver Wong wrote:
...
*: Actually, making equals "aware" of class hierarchies is slightly
trickier than that. I wanted to gloss over this in my above example, but
here's a more complete example implementation:

public class SomeClass {
final int field1;
final String field2;

/*Constructors, getters, and hashcode*/

@Override
public boolean equals(Object other) {
if (other instanceof SomeClass) {
return equals((SomeClass)other);
}
return false;
}

public boolean equals(SomeClass other) {
if (!other.getClass().equals(this.getClass())) {
return other.equals(this);
}
if (this.field1 != other.field1) {
return false;
}
if (!this.field2.equals(other.field2)) {
return false;
}
return true;
}
}

I got worried about whether this code would ever make up its mind, given a
subclass comparison. It doesn't:

public class SomeClass {
final int field1 = 0;
final String field2 = "test";

public static void main(String[] args) {
Object obj1 = new SomeClass();
Object obj2 = new SomeSubClass();
System.out.println(obj1.equals(obj2));
}


/*Constructors, getters, and hashcode*/

@Override
public boolean equals(Object other) {
if (other instanceof SomeClass) {
return equals((SomeClass)other);
}
return false;
}

public boolean equals(SomeClass other) {
if (!other.getClass().equals(this.getClass())) {
return other.equals(this);
}
if (this.field1 != other.field1) {
return false;
}
if (!this.field2.equals(other.field2)) {
return false;
}
return true;
}

private static class SomeSubClass extends SomeClass{
}
}

This program results in a StackOverflowError. obj1 passes the comparison
to obj2, which passes it back to obj1... What am I doing wrong? Are
subclasses of SomeClass required to override equals with specific rules?

The SomeSubClass must override equals(SomeClass) to define how the
subclass participates in the equality check. The idea is that classes tend
to know about their superclasses, but not their subclasses, so when
comparing for equality between two objects, the one whose class is a
subclass of the other should be "controlling" the comparison.

Consider data structures like (the following is pseudo-Java):

public Struct Point {
int x, y;
}

public Struct Point3D extends Point{
int z;
}

And for our given problem domain, we say a Point3D is only equal to a
Point if the x and y coordinates match, and if the z coordinate of the
Point3D is 0.

Rather than putting this extra logic in Point, it would probably make
more sense to put it in Point3D, but if you don't provide the subclass check
in Point, then these two calls to Equals will probably give inconsistent
results:

Point p = /*get somehow*/;
Point3D p3d = /*get somehow*/;

p.equals(p3d);
p3d.equals(p);

So I always put that subclass check, so that I am (or others are) free
to later on provide a subclass which may introduce new rules to the way
equals works across related classes.

- Oliver
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,813
Messages
2,569,699
Members
45,489
Latest member
SwethaJ

Latest Threads

Top