为什么 Kotlin 使用 == 来表示结构相等而引入 === 来表示引用相等

Why does Kotlin use == for structural equality and introduce === for referential equality

总的来说,Kotlin 中的每个设计决策都让人觉得它本身就很棒,并且提供了从 Java 的良好过渡。作为 Java 开发人员,您可以开始使用它编写代码,将 Kotlin 视为更简洁 Java 且样板文件更少的版本,然后顺利进入更高级的方面,例如函数式编程。

然而,我想知道的一件事是为什么它的设计者决定让 == 的行为与 equals 相同,然后引入 === 进行引用相等性检查。我可以想象尝试将其他 Java 开发人员纳入其中,让他们看到您的 Kotlin 代码并思考 "oh no, there are reference checks all over the place where it should be an equals call!"

离开这里的 Java 约定的想法是什么?澄清一下,我完全理解 Kotlin 中 ==equals=== 之间的区别,我只是想知道为什么。

主要原因可能是 检查对象相等性比检查对象标识更频繁,因此至少应该同样容易。

我还没有看到关于此的明确声明。但是 Kotlin 团队成员在书中 Kotlin In Action 中有一个指针。第 4.3.1 节,介绍 == 运算符,首先描述了 Java 的比较,并说:

in Java, there's the well-known practice of always calling equals, and there's the well-known problem of forgetting to do so.

在 Java 中,检查对象身份很容易:

if (firstObj == secondObj)

但是检查对象相等性比较长而且不太清楚:

if (firstObj.equals(secondObj))

— 或者更确切地说,如果您不想冒 NullPointerException 的风险:

if ((firstObj == null) ? (secondObj == null) : firstObj.equals(secondObj))

您可以看到打字和正确输入有多痛苦。 (特别是当其中一个对象是具有副作用的表达式时……)

所以很容易忘记区别,或者懒得理会,改用==。 (这可能会导致难以察觉且间歇性咬人的虫子。)

然而,

Kotlin 使最常见的操作变得更简单:它的 == 运算符使用 equals() 检查对象相等性,并且还负责空值检查。这解决了 Java 的“忘记这样做的问题”。

(虽然与 Java 代码的互操作性显然是一个主要目标,但 JetBrains 并没有限制自己尝试 看起来 像 Java;Kotlin在可能的情况下从 Java 借用,但不怕将事情变得更好。您可以看到在使用 valvar 以及声明的尾随类型时,不同的默认值对于范围界定和开放性,处理方差的不同方式,&c.)

Kotlin 的动机之一是解决 Java 中的许多问题。 (事实上​​,JetBrains 的 comparison 语言首先列出了“Kotlin 中解决的一些 Java 问题”。)因此,这似乎是更改背后的主要原因。

除了 gidds 的出色回答之外,还有其他语言 Java 对 Kotlin 产生了强烈的影响。特别是 Scala 和 C#。来自 Dmitry Jemerov 的引述(link 是给 InfoWorld 的,我不太喜欢它,但这是我找到的最好的来源):

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.

显然 == 是他们认为 Scala 正确的特性之一,因为它的工作原理完全相同(直到引用相等的名称,在 Scala 中为 eq)。

并且您可以在 Martin Odersky 作为作者之一的《Programming in Scala》一书中找到 an explanation for Scala's design

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...

另一方面,C# 允许重载 ==Equals 分开,他们最终选择了一位语言设计者 saying

The long answer is that the whole thing is weird and neither works the way it ideally ought to.