Почему проверка ссылочного равенства возвращает true, когда ссылка различна

Рассмотрим этот код:

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

Он выводит

 true true 

в то время как я ожидал

 true false 

Почему true напечатана для второго чека, так как обе ссылки разные

Ссылочное равенство заключается не в том, что имя переменной одно и то же, или к нему обращаются таким же образом, что местоположение в памяти одно и то же. Поскольку строки неизменяемы, компилятор часто может зарезервировать память заранее для них и иметь все ссылки на одну и ту же точку ценности на одно и то же место.

Неизбежность важна, потому что безопасно использовать ссылки только для чтения в тех случаях, когда ссылки на чтение / запись будут разными. Если вы неправильно используете ссылки между изменяемыми структурами данных, изменения из одного набора ссылок будут отражены в другом, что приведет к странному и неправильному поведению. Однако, если данные больше не могут измениться, вы можете сохранить как можно больше памяти, указав все на одни и те же данные.

В зависимости от того, как MetricCategory.Async.toString() , результат операции может быть произвольным. Рассмотрим следующий пример:

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

Эта реализация привела бы к true , true распечатке. Как указано, оператор === сравнивает ссылочное равенство :

оценивает значение true тогда и только тогда, когда a и b указывают на один и тот же объект.

Но почему 2 константных строковых выражения один и тот же объект? Это вызвано особенностью JVM (и других runtimes), называемой интерпретацией строк :

В информатике интернирование строк – это метод хранения только одной копии каждого отдельного строкового значения, которое должно быть неизменным. Внутренние строки делают некоторые задачи обработки строк более экономными по времени или пространству за счет того, что требуется больше времени при создании или интернировании строки. Разные значения хранятся в пуле пула.

Инсталляция строк не происходит автоматически в JVM, но ее можно запустить вручную.

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

Вышеприведенный пример String.intern true , true снова, но только потому, что мы вызвали 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 

Дальнейшее чтение:

  • Что такое интернирование строк?
  • Разница между строковым объектом и строковым литералом
  • Действительно ли интернирование строк действительно полезно?
Intereting Posts
Kotlin: В чем разница между Apply and Also Могу ли я создать метод расширения Kotlin для добавления rxJava-подписки на композитную подписку? свойство lateinit не было инициализировано Kotlin делегирует собственность ленивым, это нить локальная Почему я не могу поставить {анонимного класса на новую линию в Котлин? Как управлять потоком без .flatMap, который разбивает реактивный поток, предотвращающий работу операторов, таких как distinctUntilChanged, от работы со всем потоком Java-совместимость: как объявить константу массива компиляции в Котлине? Вторичный конструктор Котлина Невозможно вернуть факториальный результат в функцию tailrec в Котлине Как синтетически добавить активность в задний стек перед началом другого? Асинхронная работа, но получение нерешенной ссылки для ожидания Попытка написать эффективный код для обновления цвета фона с помощью Kotlin Kotlin неожиданная `неразрешенная ссылка` Как динамически масштабировать отскок потока эмиссионных выбросов? Проблема Котлина