Translated from: Equality in Kotlin Author: Suneet Agrawal Translated by : AmazingRise (Authorized by original author)
在编程中,我们经常需要比较两个变量的值是否相等,或者两个对象的引用是否一致。
Kotlin语言里,“等号”有这么几种:==
,===
与.equals
。
那么问题来了,我们该如何正确使用这些相等性判断呢?
我们来一起看一看Kotlin的几种相等性判断:
结构性相等 (“==”)
==
操作符用于比较两个变量的值。
请不要与Java中的==
相混淆。
与Java中的==
操作符不同的是:==
操作符在Kotlin中只比较值。
而Java或其它语言中,==
通常用来比较两个对象的引用。
Kotlin中,==
的否定形式是!=
,当两个变量值不同时返回真。
引用性相等(“===”)
===
操作符用于比较两个变量的引用是否一致。只有当两个变量指向同一个实例时,===
的值才为真。
===
的否定形式是!==
,当两个对象的引用不同的时候返回真。
不过对于原始类型(Primitive type)来说(例如 Int),===
等价于 ==
。
.equals 方法
equals(other: Any?)
方法是在Any
类里实现的。
并且所有类中.equals
都可以被重写(毕竟所有的类都继承于Any嘛,就像Java里的Object一样)。
.equals
方法将会比较两个变量的值。
与==
不同体现在比较Float
和Double
的时候,.equals
违背了IEEE 754 浮点数运算标准。
与IEEE 754浮点数运算标准相违背,意味着什么?
这意味着:
- NaN 等于它本身
- NaN 比任何其他元素要大,包括正无穷
POSITIVE_INFINITY
- 编译器会认为
-0.0
要比0.0
小。
一头雾水?
举个栗子
我来举几个例子解释一下。
首先,我们用这几个方法比较两个原始类型的变量(Int)。
val int1 = 10
val int2 = 10
println(int1 == int2) // true
println(int1.equals(int2)) // true
println(int1 === int2) // true
这三个println最后都会输出true
,因为对于原始类型,这三种判断都仅仅检查变量的值。
所以在这种情况下,===
输出的结果也是一样的。
现在让我们把原始类型换成包装类,看看三种方法表现出的行为。
val first = Integer(10)
val second = Integer(10)
println(first == second) //true
println(first.equals(second)) //true
println(first === second) //false
在上面的例子中,==
与.equals
输出了true
,因为他们仅仅比较了两个变量的值。
而===
比较了对象的引用,而这两个对象的引用是不同的,所以最后输出了false
。
现在我们来看另一种情况。我们自己定义一个类,然后再用这三个方法:
class Employee (val name: String)
val emp1 = Employee(“Suneet”)
val emp2 = Employee(“Suneet”)
println(emp1 == emp2) //false
println(emp1.equals(emp2)) //false
println(emp1 === emp2) //false
println(emp1.name == emp2.name) //true
println(emp1.name.equals(emp2.name)) //true
println(emp1.name === emp2.name) //true
结果显而易见。因为Employee类既不是原始数据类型,也不是包装类,这种情况下它们都会比较两个变量的引用,所以它们都返回了false
。
但是对于字符串的比较,这三个方法都只检查了字符串的内容,所以它们都返回了true
。
等等,你说==
和.equals
只比较两个对象的内容,在我们的这个例子里它们都是一样的呀
(译者注:指第一段,三个比较全都返回了false)
**这没毛病。**但内容比较仅对于data
类起作用。对于普通的类,即使两个对象内容完全一致,编译器也会认为两个对象是不同的。而对于data
类,编译器将比较对象的内容,并且在内容相同的情况下返回true
。
让我们把上面的普通类改为data
类:
data class Employee (val name: String)
val emp1 = Employee("Suneet")
val emp2 = Employee("Suneet")
println(emp1 == emp2) //true
println(emp1.equals(emp2)) //true
println(emp1 === emp2) //false
println(emp1.name == emp2.name) //true
println(emp1.name.equals(emp2.name)) //true
println(emp1.name === emp2.name) //true
这回就没问题了。最后我们看一下浮点数的比较,我们来看看“正数0”和“负数0”:
val negZero = -0.0f
val posZero = 0.0f
println(negZero == posZero) //true
println(negZero.equals(posZero)) //false
println(negZero === posZero) //true
对于Float
和Double
类的比较,.equals
方法违背了IEEE 754 浮点数运算标准,当-0.0
与0.0
比较的时候,它会返回false
。
而==
和===
都会返回true
。
最后谨记
- 因为Kotlin里没有像
String("")
这样的构造器,当比较字符串的时候,只要内容相等,都会返回true
。 - 对于
null
引用的判断,我们不必做任何优化。a == null
会被自动转换为a === null
,因为null本身也是一个引用,它将会引发引用检查(而不是值检查)。
就酱。点击这里看看我写的其他有趣文章。 或者点击这里,瞧一瞧我写的App和游戏。 另外,欢迎在你的App里面使用我的Android开源组件。 如果你需要帮助的话,可以给我发邮件: agrawalsuneet AT gmail.com。
引用: Kotlin docs
翻译完结。
作者:Suneet Agrawal. 网站:https://agrawalsuneet.github.io/agrawalsuneet/ 十分感谢作者写的文章,以及授权本人翻译,以分享给中国开发者。 原作者保留所有权利。
上次修改於 2019-05-18