EqualsIncompatibleType
An equality test between objects with incompatible types always returns false

Category
JDK
Severity
WARNING
Maturity

The problem

This checker flags all equality tests between objects o1 and o2, where o1 and o2 have incompatible types and no common superclass of o1 and o2 implements or declares an override of java.lang.Object.equals(). The result of this test is almost certainly false in most configurations and a proper equality predicate must be provided.

This check is enforced on all invocations of the following equality predicates:

If you need to check that two objects are not equal for testing purposes, we recommend that you use EqualsTester instead.

Suppression

Suppress false positives by adding an @SuppressWarnings("EqualsIncompatibleType") annotation to the enclosing element.


Positive examples

EqualsIncompatibleTypePositiveCases.java

/*
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.errorprone.bugpatterns.testdata;

/**
 * @author avenet@google.com (Arnaud J. Venet)
 */
public class EqualsIncompatibleTypePositiveCases {
  class A {}

  class B {}

  void checkEqualsAB(A a, B b) {
    // BUG: Diagnostic contains: incompatible types
    a.equals(b);
    // BUG: Diagnostic contains: incompatible types
    b.equals(a);
  }

  class C {}

  abstract class C1 extends C { public abstract boolean equals(Object o); }

  abstract class C2 extends C1 {}

  abstract class C3 extends C {}

  void checkEqualsCC1C2C3(C c, C1 c1, C2 c2, C3 c3) {
    // BUG: Diagnostic contains: incompatible types
    c3.equals(c1);
    // BUG: Diagnostic contains: incompatible types
    c3.equals(c2);
    // BUG: Diagnostic contains: incompatible types
    c1.equals(c3);
    // BUG: Diagnostic contains: incompatible types
    c2.equals(c3);
  }

  void checkStaticEqualsCC1C2C3(C c, C1 c1, C2 c2, C3 c3) {
    // BUG: Diagnostic contains: incompatible types
    java.util.Objects.equals(c3, c1);
    // BUG: Diagnostic contains: incompatible types
    java.util.Objects.equals(c3, c2);
    // BUG: Diagnostic contains: incompatible types
    java.util.Objects.equals(c1, c3);
    // BUG: Diagnostic contains: incompatible types
    java.util.Objects.equals(c2, c3);
  }

  void checkGuavaStaticEqualsCC1C2C3(C c, C1 c1, C2 c2, C3 c3) {
    // BUG: Diagnostic contains: incompatible types
    com.google.common.base.Objects.equal(c3, c1);
    // BUG: Diagnostic contains: incompatible types
    com.google.common.base.Objects.equal(c3, c2);
    // BUG: Diagnostic contains: incompatible types
    com.google.common.base.Objects.equal(c1, c3);
    // BUG: Diagnostic contains: incompatible types
    com.google.common.base.Objects.equal(c2, c3);
  }

  interface I {
    public boolean equals(Object o);
  }

  class D {}

  class D1 extends D {}

  class D2 extends D implements I {}

  void checkEqualsDD1D2(D d, D1 d1, D2 d2) {
    // BUG: Diagnostic contains: incompatible types
    d1.equals(d2);
    // BUG: Diagnostic contains: incompatible types
    d2.equals(d1);
  }

  enum MyEnum {}

  enum MyOtherEnum {}

  void enumEquals(MyEnum m, MyOtherEnum mm) {
    // BUG: Diagnostic contains: incompatible types
    m.equals(mm);
    // BUG: Diagnostic contains: incompatible types
    mm.equals(m);

    // BUG: Diagnostic contains: incompatible types
    com.google.common.base.Objects.equal(m, mm);
    // BUG: Diagnostic contains: incompatible types
    com.google.common.base.Objects.equal(mm, m);
  }
}

Negative examples

EqualsIncompatibleTypeNegativeCases.java

/*
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.errorprone.bugpatterns.testdata;

/**
 * @author avenet@google.com (Arnaud J. Venet)
 */
public class EqualsIncompatibleTypeNegativeCases {
  class A {
    public boolean equals(Object o) {
      if (o instanceof A) {
        return true;
      }
      return false;
    }
  }

  class B1 extends A {}

  class B2 extends A {}

  class B3 extends B2 {}

  void checkEqualsAB1B2B3(A a, B1 b1, B2 b2, B3 b3) {
    a.equals(a);
    a.equals(b1);
    a.equals(b2);
    a.equals(b3);
    a.equals(null);

    b1.equals(a);
    b1.equals(b1);
    b1.equals(b2);
    b1.equals(b3);
    b1.equals(null);

    b2.equals(a);
    b2.equals(b1);
    b2.equals(b2);
    b2.equals(b3);
    b2.equals(null);

    b3.equals(a);
    b3.equals(b1);
    b3.equals(b2);
    b3.equals(b3);
    b3.equals(null);
  }

  void checks(Object o, boolean[] bools, boolean bool) {
    o.equals(bool);
    o.equals(bools[0]);
  }

  void checkJUnit(B1 b1, B2 b2) {
    org.junit.Assert.assertFalse(b1.equals(b2));
  }

  void checkStaticEquals(A a, B1 b1, B2 b2, B3 b3) {
    java.util.Objects.equals(a, a);
    java.util.Objects.equals(a, b1);
    java.util.Objects.equals(a, b2);
    java.util.Objects.equals(a, b3);
    java.util.Objects.equals(a, null);

    java.util.Objects.equals(b1, b3);
    java.util.Objects.equals(b2, b3);
    java.util.Objects.equals(b3, b3);
    java.util.Objects.equals(null, b3);
  }

  void checkGuavaStaticEquals(A a, B1 b1, B2 b2, B3 b3) {
    com.google.common.base.Objects.equal(a, a);
    com.google.common.base.Objects.equal(a, b1);
    com.google.common.base.Objects.equal(a, b2);
    com.google.common.base.Objects.equal(a, b3);
    com.google.common.base.Objects.equal(a, null);

    com.google.common.base.Objects.equal(b1, b3);
    com.google.common.base.Objects.equal(b2, b3);
    com.google.common.base.Objects.equal(b3, b3);
    com.google.common.base.Objects.equal(null, b3);
  }

  class C {}

  abstract class C1 extends C { public abstract boolean equals(Object o); }

  abstract class C2 extends C1 {}

  abstract class C3 extends C1 {}

  void checkEqualsC1C2C3(C1 c1, C2 c2, C3 c3) {
    c1.equals(c1);
    c1.equals(c2);
    c1.equals(c3);
    c1.equals(null);

    c2.equals(c1);
    c2.equals(c2);
    c2.equals(c3);
    c2.equals(null);

    c3.equals(c1);
    c3.equals(c2);
    c3.equals(c3);
    c3.equals(null);
  }

  interface I {
    public boolean equals(Object o);
  }

  class E1 implements I {}

  class E2 implements I {}

  class E3 extends E2 {}

  void checkEqualsIE1E2E3(I e, E1 e1, E2 e2, E3 e3) {
    e.equals(e);
    e.equals(e1);
    e.equals(e2);
    e.equals(e3);
    e.equals(null);

    e1.equals(e);
    e1.equals(e1);
    e1.equals(e2);
    e1.equals(e3);
    e1.equals(null);

    e2.equals(e);
    e2.equals(e1);
    e2.equals(e2);
    e2.equals(e3);
    e2.equals(null);

    e3.equals(e);
    e3.equals(e1);
    e3.equals(e2);
    e3.equals(e3);
    e3.equals(null);
  }

  interface J {}

  class F1 implements J {}

  abstract class F2 { public abstract boolean equals(J o); }

  void checkOtherEquals(F1 f1, F2 f2) {
    f2.equals(f1);
  }
}