Why is this an issue?

The Any#equals(other: Any?) method is used to compare two objects to see if they are equal.

The other parameter’s type is Any?, this means that an object of any type, as well as null, can be passed as an argument to this method.

Any class overriding Any#equals(other: Any?) should respect this contract, accept any object as an argument, and return false when the argument’s type differs from the expected type. The other parameter’s type can be checked using the is operator or by comparing the javaClass field:

override fun equals(other: Any?): Boolean {
  // ...
  if (other?.javaClass != this.javaClass) {
    return false
  }
  // ...
}

However, it is an issue to assume that the equals method will only be used to compare objects of the same type. Casting the other parameter without a prior test will throw a ClassCastException instead of returning false.

class MyClass {
  override fun equals(other: Any?): Boolean {
    val that = other as MyClass // may throw a ClassCastException
    // ...
  }
  // ...
}

This rule raises an issue when other parameter’s type has not been tested before a cast operation.

How to fix it

Ensure the other parameter’s type is checked by comparing other?.javaClass and this.javaClass, or use the is operator to test `other’s type.

Code examples

Noncompliant code example

class MyClass {
  override fun equals(other: Any?): Boolean {
    if (this === other) {
        return true
    }
    val that = other as MyClass // Noncompliant, may throw a ClassCastException
    // ...
  }
  // ...
}

Compliant solution

class MyClass {
  override fun equals(other: Any?): Boolean {
    if (this === other) {
        return true
    }
    if (other?.javaClass != this.javaClass) {
      return false
    }
    val that = other as MyClass // Compliant, other's type is MyClass
    // ...
  }
  // ...
}

Resources

Documentation