I'm not certain of the reason for this error, but it might be related to a deeper problem with your approach…
In Kotlin (and Java), the equals() method has a fairly tight specification. One condition is that it must be symmetric: whenever a and b are not null, a.equals(b) must always give the same result as b.equals(a).
But your implementation fails this test, because CS("abc").equals("abc") returns true, while "abc".equals(CS("ABC")) is false. That's because your class knows about CharSequences such as String, but String does not know about your class.
There's no easy way around that. In general, it's much safer to allow instances of a class to equal only instances of that class. If you control both classes, then there are ways around that, but they're quite subtle and involved. (Perhaps the best explanation is by Martin Odersky et al.)
So most implementations of equals() tend to work along these lines:
override fun equals(other: Any?)
= (other is ThisClass)
&& field1 == other.field1
&& field2 == other.field2
// ...
As I said, I don't know why the Kotlin compiler is complaining in your case. It may be that it has spotted something of this problem, or it may be something unrelated. But I don't think you're going to be able to fix your program in a way that equality checks will do what you want, so perhaps it's best to take this as a hint to try a slightly different approach!
Answer from gidds on Stack OverflowI'm not certain of the reason for this error, but it might be related to a deeper problem with your approach…
In Kotlin (and Java), the equals() method has a fairly tight specification. One condition is that it must be symmetric: whenever a and b are not null, a.equals(b) must always give the same result as b.equals(a).
But your implementation fails this test, because CS("abc").equals("abc") returns true, while "abc".equals(CS("ABC")) is false. That's because your class knows about CharSequences such as String, but String does not know about your class.
There's no easy way around that. In general, it's much safer to allow instances of a class to equal only instances of that class. If you control both classes, then there are ways around that, but they're quite subtle and involved. (Perhaps the best explanation is by Martin Odersky et al.)
So most implementations of equals() tend to work along these lines:
override fun equals(other: Any?)
= (other is ThisClass)
&& field1 == other.field1
&& field2 == other.field2
// ...
As I said, I don't know why the Kotlin compiler is complaining in your case. It may be that it has spotted something of this problem, or it may be something unrelated. But I don't think you're going to be able to fix your program in a way that equality checks will do what you want, so perhaps it's best to take this as a hint to try a slightly different approach!
The == operator in Kotlin doesn't work if the types on both sides of the operation are known and different from each other.
For example:
3 == "Hello"
true == 5.0
// and so on
Will give a compile error because the compiler infers that operands are instances of different classes and therefore cannot be equal.
The only exception is if one side of the operator is a subclass of the other:
open class A
class B: A()
class C: A()
val c = C()
val b = B()
val a = A()
c == b
c == a // good
a == b // also good
In this case, c == b will give a compile error, while the other two operations will not.
That is why when you cast one side of the operation to Any it no longer gives an error since everything is a subtype of Any.
Videos
Is there any way to enforce a generic type to have the equals() method overriden, without implementing a custom interface in every class used? I have a use case where I need to be able to compare values of parameters with the same generic type.
Defining equals and hashCode is considered somewhat useless on object declarations that have no explicit supertype, according to this issue. Probably correct equals+hashCode implementations on objects have few use cases.
There's even an IDE inspection that shows a warning when you try to do so:
The warning is not there when the object has a declared supertype.
However, I don't think that some technical reason stops Kotlin from resolving the overloaded operator, and the whole behaviour is strange, so I filed an issue in Kotlin issue tracker.
As for now (Kotlin 1.0.2 EAP), even with declared supertype you can only check object's equality with instances of exactly the same declared type which it has as a supertype:
object SomeObject : List<Int> by listOf() { ... }
SomeObject == listOf(1, 2, 3) // OK
SomeObject == arrayListOf(1, 2, 3) // not resolved (why?)
object MyObject : Any() { ... }
MyObject == 1 // error
MyObject == 1 as Any // OK o_O
object AnotherObject { ... }
AnotherObject == 1 as Any // works! Probably Any is supertype here
As to defining equals as an extension function: no, you can't do it because extensions are resolved statically and are shadowed by members (there's a similar question about toString).
i think your implementation is the right approach
consider this:
class MyClass {
companion object {
private val NUMBER: Int = 5
private val STRING: String = "foo"
override fun equals(other: Any?): Boolean {
when(other){
is MyClass -> {
return this.NUMBER == other.NUMBER &&
this.STRING == other.STRING
}
else -> return false
}
}
}