Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does referential equality check return true when reference is different

Tags:

kotlin

Consider this code:

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

It outputs

true
true

while i was expecting

true
false

Why is true printed for the 2nd check since both references are different

like image 593
pdeva Avatar asked Jul 21 '16 22:07

pdeva


2 Answers

Reference equality isn't that the variable name is the same, or it's accessed in the same way, it's that the location in memory is the same. As Strings are immutable, the compiler is often able to reserve memory in advance for them, and have all references to the same value point to the same place.

The immutability is important, because it's safe to share read-only references in cases where read/write references would be different. If you improperly share references between mutable data structures, modifications from one set of references will be reflected in the other, leading to strange and incorrect behavior. However, if the data can no longer change, you're free to save as much memory as possible by having everything point to the same data.

like image 75
Edward Peters Avatar answered Oct 24 '22 10:10

Edward Peters


Depending on how MetricCategory.Async.toString() is implemented the outcome of the operation might be arbitrary. Consider following example:

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

This implementation would result in true, true printed out. As documented the === operator compares referential equality:

evaluates to true if and only if a and b point to the same object.

But why are the 2 constant string expressions the same object? This is caused by a feature of JVM (and other runtimes) called string interning:

In computer science, string interning is a method of storing only one copy of each distinct string value, which must be immutable. Interning strings makes some string processing tasks more time- or space-efficient at the cost of requiring more time when the string is created or interned. The distinct values are stored in a string intern pool.

String interning does not happen automatically in JVM but it can be triggered manually.

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

The above example would print true, true again but only because we've called String.intern.

Consider below examples:

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

Further reading:

  • What is String interning?
  • Difference between string object and string literal
  • Is string interning really useful?
like image 37
miensol Avatar answered Oct 24 '22 08:10

miensol