Одна из лучших особенностей Kotlin — работа с нулевыми значениями, одним из самых распространенных источников ошибок в Java и других языках программирования. Однако в Kotlin вы можете нарушить этот контракт, используя !!
, что приводит к NullPointerException
без подробностей о том, почему и что произошло.
Понимаем !!
Вкратце, он принудительно утверждает, что данный тип не является null
. Если наше значение равно null
, мы получим NPE
. Вот пример:
val name: String? = null val length = name!!.length // Throws NPE(NullPointerException)
Почему же это так важно?
- Он нечитабелен, его трудно поддерживать. Если новый программист посмотрит на код с
!!
он спросит себя: «Что, черт возьми, здесь происходит???», а ответа не найдет. - Это хак, который подойдет для коротких скриптов, но не для готовых к производству приложений.
- Это ненужный риск. В Kotlin есть множество лучших способов обработки нулевого значения с помощью
?.
,?:
,умного приведения типов
и дажеrequireNotNull
(который выдает репрезентативное сообщение).
Альтернативы !!
1. Безопасный оператор ?.
Если вы хотите, чтобы значение все еще было нулевым или не выполняло работу, если оно нулевое, то используйте оператор ?.
:
val name: String? = null val length = name?.length // Safe, returns null if name is null
2. Оператор Элвис ?:
Если вам нужно сделать что-то по-другому в случае, когда значение равно null
, смело используйте оператор Элвис ?:
:
val name: String? = null val length = name?.length ?: 0 // Returns 0 if name is null
3. Умное приведение типов
Один из самых простых способов уменьшить вероятность возникновения ошибок с нулями — не принимать их вовсе. Вы можете ограничить свои методы, чтобы они не принимали нулевые значения:
fun doSomething(name: String) { // name will always be non-nullable ... }
4. requireNotNull
с описательным сообщением
Если вы не можете уменьшить nullability и значение должно присутствовать, вы можете подумать, что это хорошая идея использовать !!
, но в Kotlin в этом случае следует использовать requireNotNull
. Таким образом, вы сможете получить описательное сообщение в отчете о сбое вместо NPE
без подробной информации о том, что произошло (особенно если это вложено в лямбду):
val name: String? = null val length = requireNotNull(name) { "Name cannot be null" }.length
Резюме
Использование !!
— это плохая практика; множество альтернатив покрывают все остальные возможные случаи. Использование этого оператора — это хак и сокращение, которое приведет к обратному результату и не сэкономит вам много времени даже в момент написания. Не стоит использовать его, если только вы не пишете короткий личный скрипт.
Комментарии
- Я не согласен с мнением о том, что использование
!!
— это изначально плохая практика. В некоторых ситуациях это разумный способ утверждать, что значение не должно быть null. Если это приводит к NPE, то действует как защитная сетка, отлавливая неожиданные крайние случаи на ранней стадии- Для таких ситуаций, как эта, вы должны использовать
requireNotNull
, чтобы проверить, если значение не должно бытьnull
. Это приведет к исключению, но это также даст вам описательное сообщение в логе, что произошло и где,. Это то, что!!
не всегда дает и может доставить много хлопот для поиска решения.
- Для таких ситуаций, как эта, вы должны использовать
- Неправда. NPE — это находка, когда нужно обеспечить согласованность состояния приложения. NPE просто отказывает приложению в продолжении работы при возникновении ненормальной ситуации.
?
позволяет приложению продолжить работу при обнаружении ошибки, что очень плохо… NPE при умелом использовании может радикально улучшить согласованность, и это прекрасный инструмент в разработке.