Использование оператора безопасного вызова Kotlin

Предположим, у меня есть следующий код:

fun main() {
    val userName: String? = "Dmitry" // line 1
    userName?.takeLast(2)?.reversed() // line 2
    userName?.plus("kotlin").plus("lang") // line 3
}

Я не могу понять причину использования оператора Элвиса раньше reversed() - почему нельзя использовать просто . ? Как я вижу в документе, takeLast() всегда предоставляет String, а не String?. Тот же сценарий работает для строки 3 в моем примере -> plus() всегда возвращает строку, поэтому Элвис перед вторым plus() не нужен?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
76
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Из характеристики:

a?.c точно так же, как

when (val $tmp = a) {
    null -> null
    else -> { $tmp.c }
}

Другими словами, userName?.takeLast(2)?.reversed() то же самое, что:

val $tmp1: String? = when (userName) {
    null -> null
    else -> userName.takeLast(2)
}
val $tmp2: String? = when ($tmp1) {
    null -> null
    else -> $tmp1.reversed()
}
// ...and so on.

Приведенное выше расширение показывает, что каждый шаг сам по себе может вернуться null, а значит, необходимо повторить ?..

Что касается .plus(), то первый .plus() — это String.plus() , тогда как второй .plus() — это String?.plus(). Последний будет рассматривать null как строку "null", но в случае с первым это не так.

Если userName — это null, userName.plus() позвонит String?.plus(). Только из-за ?. вместо этого вызывается String.plus(). Чтобы применить то же вышеупомянутое расширение:

val $tmp: String? = when (userName) {
    null -> null
    else -> userName.plus("kotlin")  // String.plus()
}
$tmp.plus("lang")  // String?.plus()
Ответ принят как подходящий

Поскольку перед ?. стоит takeLast(), результирующее выражение оценивается как строка, допускающая значение NULL. Поскольку userName может иметь значение null, takeLast() может даже не вызываться, и при вызове takeLast() у вас просто будет значение null.

В последней строке вы вызываете функцию расширения, определенную для приемника String, допускающего значение NULL, поэтому ее не нужно вызывать по условию. Он определен как fun String?.plus, поэтому его можно вызывать непосредственно на вводе, допускающем значение NULL.

Кстати, ?. не называется оператором Элвиса. Это называется оператором безопасного вызова. Оператор Элвиса — ?: и делает что-то другое.

Спасибо за примечание относительно именования операторов — я отредактировал заголовок. А еще спасибо за пояснения - теперь мне все понятно

Dmitry 26.05.2024 20:39

Другие вопросы по теме

Является ли неопределенным поведение передача указателя на несконструированный объектstreambuf в конструктор ostream?
Является ли рекурсивный вызов main из его собственных параметров (злоупотребление sizeof с помощью VLA) стандартом C99?
Перегрузка и нестабильность
До C++11 «правило одного определения» нарушалось при инициализации членов класса нестатических и неконстантных переменных. Почему?
Что произойдет, если я вызову allocate_at_least(0) согласно стандарту C++23?
Почему изменяемая лямбда преобразуется в указатель на функцию вместо вызова оператора()?
Могут ли `::` и `*`, образующие тип указателя-члена, происходить из разных расширений макроса или они должны появляться как один токен?
Определено ли вычисление смещений указателей байтов между элементами композиции, не являющимися массивами?
Как проверить, является ли конструктор явно дефолтным
Требуется ли создание экземпляра для неиспользуемого, но инициализированного статического элемента данных const int шаблона класса?