Оператор !! в Kotlin: что это и как не сломать код

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Котлин, нулли и двойной восклицательный знак: знакомство

Вы когда-нибудь писали на Kotlin, компилятор ругался на потенциальный null, а вы в ответ ставили два восклицательных знака — !! — и шли дальше? Если да, то вы уже знакомы с героем нашей статьи — оператором double bang (он же оператор !! в Kotlin).

Этот символ — одновременно спаситель и опасный инструмент. Он говорит компилятору: «Заткнись, я знаю, что делаю, эта переменная точно не null». Но если вы ошиблись — приложение упадёт с NullPointerException. Звучит страшно? На самом деле, это просто мощный механизм, который нужно понимать и использовать с умом.

В этом руководстве мы разберём, что такое double bang Kotlin, как он работает, где его применять, а где лучше поискать альтернативы. Вы узнаете, как не выстрелить себе в ногу и писать надёжный код.

Проблема, которую решает !! : Null Safety в Kotlin

Kotlin создавался как язык, который должен был убить NullPointerException (NPE). И у него это почти получилось. В Kotlin типы по умолчанию не могут быть null. Вы просто не сможете присвоить null переменной типа String — компилятор не даст.

var name: String = "Алиса"
name = null // Ошибка компиляции!

Но реальный мир полон неопределённости. Данные приходят из базы, от пользователя, из сети. Поэтому Kotlin ввёл nullable-типы — типы, которые могут быть null. Они обозначаются вопросительным знаком: String?.

var nullableName: String? = "Боб"
nullableName = null // Теперь можно

И тут начинается магия. Вы не можете просто взять и вызвать метод у nullable-переменной:

val length = nullableName.length // Ошибка! "Only safe (?.) or non-null asserted (!!.) calls are allowed"

Kotlin заставляет вас обрабатывать null. Это гениально — вы не забываете проверить. Но иногда вы точно знаете, что значение не null. И вот тут на сцену выходит оператор двойного восклицательного знака Kotlin.

Что такое оператор !! (double bang)?

Оператор !! в Kotlin — это оператор принудительного утверждения non-null. Он берёт nullable-значение и преобразует его в non-null тип. Если значение оказалось null — выбрасывается KotlinNullPointerException.

Синтаксически он выглядит как два восклицательных знака после переменной:

val nonNullValue = nullableValue!!

Как это работает на примере

Представьте, что у вас есть функция, которая ищет пользователя по ID. Она возвращает User?, потому что пользователя может не быть. Но вы точно знаете, что в вашей тестовой среде пользователь с ID = 1 существует.

data class User(val name: String)

fun findUser(id: Int): User? {
    // Какая-то логика поиска
    return if (id == 1) User("Анна") else null
}

fun main() {
    val user = findUser(1)
    // Мы уверены, что user не null
    val userName = user!!.name
    println(userName) // Выведет: Анна
}

В этом примере double bang Kotlin сработал корректно, и мы получили имя. Но если бы ID был 2, приложение упало бы с исключением.

Когда использовать !! (и когда категорически нельзя)

Оператор !! (double bang) — это как красная кнопка. Её можно нажимать, но только если вы на 146% уверены. Давайте разберём легитимные сценарии.

Когда можно (и нужно) использовать !!

  1. Взаимодействие с Java-кодом. Java не знает о nullable-аннотациях. Если Java-метод возвращает объект, Kotlin видит его как Platform Type (например, String!). Вы можете обращаться с ним как угодно. Но если вы уверены, что Java-метод не вернёт null — используйте !!.
  2. В тестах. В unit-тестах вы часто создаёте объекты вручную и точно знаете, что они не null. !! здесь уместен, чтобы не писать лишние проверки.
  3. В конструкторах Android View Binding. Типичный код: binding!!.textView.text = "Привет". Вы уверены, что binding не null, потому что он инициализирован в onCreateView.
  4. Когда вы только что проверили на null. Иногда компилятор не может "догнать" логику, хотя она очевидна разработчику.
val list: List<String>? = getList()
if (list != null) {
    // Здесь list smart-cast в List<String>, можно без !!
    println(list.size)
} else {
    // А вот здесь мы точно знаем, что list не null, но компилятор не в курсе
    // Используем !! как "я знаю лучше"
    val fallbackList = emptyList<String>()
    println(fallbackList!!.size) // Сомнительно, но можно
}

Когда НЕЛЬЗЯ использовать !!

  • В пользовательском вводе. Пользователь может ввести что угодно. Никогда не используйте !! для данных, которые пришли от пользователя, из сети или из базы данных.
  • В публичных API. Если вы пишете библиотеку или функцию, которую будут вызывать другие — не используйте !!. Вы не знаете, какие данные передадут.
  • В цепочках вызовов. user!!.address!!.city!!.name — это катастрофа. Одна ошибка — и всё упадёт. Используйте ?.let или ?:.
  • Для "красивости". Если вы ставите !! просто чтобы компилятор замолчал — вы делаете что-то не так. Потратьте 5 минут на нормальную обработку null.

Альтернативы оператору !!

Kotlin предлагает несколько элегантных способов работы с nullable-типами, которые безопаснее, чем double bang Kotlin. Давайте сравним.

1. Безопасный вызов (?. )

val length = nullableName?.length // Если nullableName null, length будет null

Идеально для цепочек: user?.address?.city. Если что-то null — цепочка обрывается, возвращается null.

2. Оператор Элвиса (?: )

val name = nullableName ?: "Гость" // Если null, используем значение по умолчанию

Блоги

Рекомендаций по книгам