epoche
epoche

Reputation: 419

Overriding equals in Kotlin

CS implements kotlin.CharSequence. The essence of it is here:

class CS  (val sequence: CharSequence = "") : CharSequence {
... override get/length in interface CharSequence 
    override fun equals(other: Any?): Boolean =
            (this === other) || ((other is String) && this.sequence.equals(other))
}

The compiler objects to CS("hello") == "hello" as: Operator '==' cannot be applied to 'CS' and 'String'. It has no problems with CS("hello") == "hello" as Any or CS("hello").equals("hello") which both work.

What am I doing wrong?

Upvotes: 31

Views: 33323

Answers (3)

Eric Martori
Eric Martori

Reputation: 2975

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.

Upvotes: 3

Piotr Wittchen
Piotr Wittchen

Reputation: 3922

@Michael mentioned in the comment that this operator is valid, so you can go to the answer below:

I think, the error you get may be related to the fact that Kotlin has problems with inferring your data type. You are providing Any type as a parameter for the equals(...) method and comparing it with your class (this). Maybe try to cast the type like that:

this == (other as String)

or

this == (other as CharSequence)

I'm not sure if that's the correct approach, but maybe it will be a hint for you.

Upvotes: 0

gidds
gidds

Reputation: 18567

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!

Upvotes: 37

Related Questions