Kotlin字符串场景下引用相等与结构相等的相关疑问咨询
提问内容
我目前对*structural equality(结构相等)的理解是,它会比较对象的类型与内容。
对于Referential equality(引用相等)而言,它比较的是两个对象的内存地址。另外如果是primitive type(原始类型)*的var变量,只要内容相同,引用相等比对结果就会为true。
根据官方文档说明:
数字、字符和布尔值在运行时可以表示为原始值,但对用户来说,它们看起来和普通类没有区别。
所以我认为String在运行时应当按照对象处理。
但我在实际测试中,对两个内容相同的字符串做引用相等比对时,返回结果为true,测试代码如下:
fun main(){ val name1 = "123" val name2 = "123" println(name1 == name2) //true, structural equality which is same type and same content (same as equals) // checks if left and right are same object --> referential equality var name3 = "123" println(name1 === name3) //true ???????? // should print false. String is basic type and not primitve var Arr: IntArray = intArrayOf(1,2,3) var Arr2: IntArray = intArrayOf(1,2,3) println(Arr === Arr2) // prints false, arrays are basic type }
另外我还有一个疑问:Kotlin中的两种相等性比对为什么没有区分val和var?二者本质上属于不同类型的对象。
麻烦大家指出我的理解误区,我应该是忽略了某个很基础的知识点,多谢。
解答
1. 为什么内容相同的字符串字面量引用相等比对返回true
这是JVM平台的字符串常量池优化导致的,和字符串本身是不是原始类型无关:
- 你代码中所有直接写死的
"123"这类编译期可确定的字符串字面量,JVM会自动将其存入字符串常量池,相同内容的字面量会复用池中的同一个对象,所以name1、name2、name3实际上指向的是同一个对象,===比对自然返回true。 - 如果要验证字符串的引用差异,可以使用运行时生成的字符串,测试如下:
val name1 = "123" val name4 = String(charArrayOf('1','2','3')) // 运行时新建的字符串对象 println(name1 == name4) // 输出true,结构相等 println(name1 === name4) // 输出false,引用不等,符合你的预期
至于IntArray的比对结果为false是正常的:数组对象不会被JVM自动缓存,每次调用intArrayOf都会生成新的堆对象,引用自然不同。
2. 为什么相等性比对不区分val和var
你对val和var的理解存在本质偏差:
- val和var只是变量的修饰符,仅用来限制变量本身的可赋值性:val修饰的变量不能被重新赋值,var修饰的变量可以被重新赋值,二者只是对象引用的持有者,和它们指向的对象的类型、属性没有任何关系。
- 两种相等性比对判断的是两个变量指向的对象的相等性,和变量本身是val还是var完全无关,自然不需要做区分。
3. 补充原始类型的运行时逻辑
Kotlin官方提到的数字、字符、布尔值可被表示为原始值,是JVM平台的专属优化:当这类类型的变量没有被当做对象使用(比如没有调用方法、没有声明为可空类型)时,JVM会将其映射为不存在引用概念的原始类型,此时的===本质就是值比对,所以相同值的比对结果为true,这个和字符串常量池的优化是完全不同的两个机制。
内容的提问来源于stack exchange,提问作者HARSHIT BAJPAI
相关产品推荐
相关产品推荐

