1

When NetBeans autogenerates an equals method it is in the following form:

@Override
public boolean equals(Object obj) {
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    final MyClass other = (MyClass)obj;
    if (!Object.equals(this.field, other.field))
        return false;
    return true;
}

This seems quite verbose. Java's instanceof returns false for null values so why not simplify the initial tests to:

if (!(obj instanceof MyClass))
    return false;

I'm also unclear on why this code would explicitly use this.field in the field comparisons (presumably to avoid confusion) but then wouldn't use this.getClass() for the same reason?

Are there any advantages at all to NetBean's form?

sprinter
  • 27,148
  • 6
  • 47
  • 78

1 Answers1

4

There's a big difference between instanceof and class comparison. Remember that (for example) a HashMap instance is instanceof AbstractMap, but AbstractMap's equals shouldn't consider other an AbstractMap if it's actually a HashMap. instanceof tests allow for an instance of the type or any of its subtypes and are therefore not symmetric. equals requires symmetry. (Example below.)

The Netbeans code does seem rather stilted and could be written more concisely, but the operations, in the order given, make sense as a default starting point. (Although I'd've included a this check.) It's a rare pair of classes that can have usefully have equal instances, so using getClass for a default implementation is reasonable.

Just to remind us all of the contract:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null reference values x andy, multiple invocations ofx.equals(y)consistently returntrueor consistently returnfalse, provided no information used inequals` comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.

In my early Java days in the 90's I broke this contract (unwittingly), and based on the answers to this question, I'm not the only one. I used instanceof (and super.equals), like this:

class Parent {
    private int foo;
    protected int getFoo() {
        return foo;
    }

    Parent(int f) {
        this.foo = f;
    }

    // ...

    @Override
    public boolean equals(Object obj) {
        // WRONG, see below
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof Parent)) {
            return false;
        }
        return this.foo == ((Parent)obj).foo;
    }
}

class Child extends Parent {
    private int bar;

    Child(int f, int b) {
        super(f);
        this.bar = b;
    }

    // ...

    @Override
    public boolean equals(Object obj) {
        // WRONG, see below
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof Child)) {
            return false;
        }
        return super.equals(obj) && this.bar == ((Child)obj).bar;
    }
}

The problem there is that it's not symmetric:

Parent p = new Parent(1);
Child  c = new Child(1, 2);
System.out.println(p.equals(c)); // true
System.out.println(c.equals(p)); // false

That violates the contract.

Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Actually objects of different types **can** be equal according to the `equals()` contract, rare as it would be. – Kayaman Oct 09 '15 at 06:59
  • @Kayaman: I'll have to take your word for it, but it would be a rare case regardless. I've reworded slightly. – T.J. Crowder Oct 09 '15 at 07:02