当引用不同时,为什么引用相等性检查返回true

时间:2016-07-21 22:23:22

标签: kotlin

考虑以下代码:

fun main(args : Array<String>) {
    println("Async" == MetricCategory.Async.toString())
    println("Async" === MetricCategory.Async.toString())
}

输出

true
true

虽然我在期待

true
false

为什么第二次检查会打印true,因为两个引用都不同

2 个答案:

答案 0 :(得分:4)

引用相等不是变量名相同,或者以相同的方式访问它,它是内存中的位置是相同的。由于字符串是不可变的,编译器通常可以提前为它们保留内存,并且所有对相同值的引用都指向同一个地方。

不可变性很重要,因为在读/写引用不同的情况下共享只读引用是安全的。如果您在可变数据结构之间不正确地共享引用,则来自一组引用的修改将反映在另一组引用中,从而导致奇怪和不正确的行为。但是,如果数据无法再更改,您可以通过让所有内容指向相同的数据来尽可能多地保存内存。

答案 1 :(得分:3)

根据MetricCategory.Async.toString()的实施方式,操作的结果可能是任意的。请考虑以下示例:

class MetricCategory {
    object Async {
        override fun toString(): String {
            return "Async"
        }
    }
}

此实施会导致truetrue打印出来。据记录,===运算符会比较referential equality

  当且仅当a和b指向同一个对象时,

的计算结果为真。

但为什么2个常量字符串表达式是同一个对象?这是由名为string interning的JVM(和其他运行时)的一个特性引起的:

  

在计算机科学中,字符串实习是一种只存储一个的方法   每个不同字符串值的副本,必须是不可变的。实习   字符串使一些字符串处理任务更多时间或   节省空间的代价是在字符串需要更多时间   创建或实习。不同的值存储在字符串实习生中   池。

String interning does not happen automatically in JVM但可以手动触发。

class MetricCategory {
    object Async {
        override fun toString(): String {
            val result = "a".toUpperCase() + "SYNC".toLowerCase()
            return result.intern()
        }
    } 
 }

以上示例将再次打印truetrue,但仅打印,因为我们已调用String.intern

请考虑以下示例:

println("Async" == "Async") // true, obviously
println("Async" === "Async") // true, string interning for literals
println("Async" == java.lang.String("Async").toString())// true, obviously
println("Async" === java.lang.String("Async").toString()) // false, new instance on the right
println("Async" === java.lang.String("Async").toString().intern()) // true, right changed to shared instance

进一步阅读: