Reputation: 11999
In general every design decision in Kotlin feels like it's both great in its own right and offers a nice transition from Java. As a Java developer you can start coding in it thinking of Kotlin as a more concise Java with less boilerplate, then smoothly move into more advanced aspects like functional programming.
The one thing I'm wondering about however is why its designers decided to make ==
behave identically to equals
and then introduce ===
for the referential equality check. I can imagine trying to bring other Java developers into the fold, having them see your Kotlin code and thinking "oh no, there are reference checks all over the place where it should be an equals call!"
What's the thought process for moving away from the Java convention here? Just to make it clear, I understand perfectly well what the difference is between ==
or equals
and ===
in Kotlin, I'm just wondering why.
Upvotes: 3
Views: 407
Reputation: 170713
In addition to gidds' excellent answer, there are other languages than Java which strongly influenced Kotlin. In particular, Scala and C#. A quote from Dmitry Jemerov (the link is to InfoWorld, which I don't much like, but that's the best source I've found):
We've looked at all of the existing JVM languages, and none of them meet our needs. Scala has the right features, but its most obvious deficiency is very slow compilation.
Clearly ==
was one of those features they felt Scala got right, because it works exactly the same (up to the name for reference equality, which is eq
in Scala).
And you can find an explanation for Scala's design in the Programming in Scala book, which has Martin Odersky as one of the authors:
As mentioned in Section 11.2, the definition of equality is different in Scala and Java. Java has two equality comparisons: the == operator, which is the natural equality for value types and object identity for reference types, and the equals method, which is (user-defined) canonical equality for reference types. This convention is problematic, because the more natural symbol, ==, does not always correspond to the natural notion of equality. When programming in Java, a common pitfall for beginners is to compare objects with == when they should have been compared with equals. For instance, comparing two strings x and y using "x == y" might well yield false in Java, even if x and y have exactly the same characters in the same order.
Scala also has an equality method signifying object identity, but it is not used much. That kind of equality, written "x eq y", is true if x and y reference the same object. The == equality is reserved in Scala for the "natural" equality of each type. For value types, == is value comparison, just like in Java. For reference types, == is the same as equals in Scala. You can redefine the behavior of == for new types by overriding the equals method, which is always inherited from class Any...
On the other hand, C# allowed overloading ==
separately from Equals
, and they ended up with one of language designers saying
The long answer is that the whole thing is weird and neither works the way it ideally ought to.
Upvotes: 3
Reputation: 18537
The major reason is probably that object equality is checked much more often than object identity, so it should be at least as easy.
I haven't seen a definitive statement on this. But there's a pointer in the book Kotlin In Action, by members of the Kotlin team. Section 4.3.1, introducing the ==
operator, first describes Java's comparisons and says that:
in Java, there's the well-known practice of always calling
equals
, and there's the well-known problem of forgetting to do so.
In Java, checking object identity is easy:
if (firstObj == secondObj)
But checking object equality is longer and rather less clear:
if (firstObj.equals(secondObj))
— or rather, if you don't want to risk a NullPointerException:
if ((firstObj == null) ? (secondObj == null) : firstObj.equals(secondObj))
You can see how much more of a pain that is to type, and to get right. (Especially when one of those objects is an expression with side-effects…)
So it's easy to forget the difference, or not be bothered, and use ==
instead. (Which is likely to cause bugs that are subtle, hard to spot, and bite intermittently.)
Kotlin, however, makes the most common operation the easier one: its ==
operator checks object equality using equals()
, and takes care of null-checking too. This fixes Java's ‘problem of forgetting to do so’.
(And although interoperability with Java code was clearly a major goal, JetBrains didn't restrict themselves to trying to look like Java; Kotlin borrows from Java where possible, but isn't afraid to change things for the better. You can see that in the use of val
and var
and trailing types for declarations, the different defaults for scoping and openness, the different ways variance is handled, &c.)
One of Kotlin's motivations was to fix many of the problems in Java. (In fact, JetBrains' comparison of the languages starts by listing ‘Some Java issues addressed in Kotlin’.) So it seems likely that this is a major reason behind the change.
Upvotes: 10