Полное руководство по обработке пустых ссылок в Kotlin
Обзор
В этой статье мы рассмотрим нулевую безопасность в Котлине.
Любой язык программирования, имеющий концепцию нулевой ссылки, вызывает исключение NullPointerException. Это было названо ошибкой на миллиард долларов. (Вики)
Тип, допускающий и не допускающий значения null
Kotlin стремится устранить риск NullPointerException. Он различает ссылки, допускающие и не допускающие значения NULL, как часть своей системы типов.
В Kotlin по умолчанию все переменные не допускают значения NULL. Мы не можем присвоить переменной значение null, потому что это приведет к ошибке компиляции:
var country: String = "India" country = null //compilation error
Чтобы определить переменную, допускающую значение NULL, мы должны добавить вопросительный знак (?) к объявлению типа:
var city: String? = "Kolkata" city = null
Мы можем вызвать метод или получить доступ к свойству переменной, не допускающей значения NULL. Однако в случае переменных, допускающих значение NULL, нам необходимо явно обработать случай NULL. В противном случае он выдаст ошибку компиляции, поскольку Kotlin знает, что переменная содержит пустые ссылки:
val a : String = country.length val b : String = city.length //compilation error
Давайте посмотрим, как можно безопасно обрабатывать пустые ссылки в Kotlin.
Работа с типами, допускающими значение NULL
Проверка нуля
Мы можем использовать выражение if-else для явной проверки переменных, допускающих значение NULL. Этот параметр работает только в том случае, если переменная неизменна. В зависимости от сложности условий это также может привести к вложенным выражениям.
Давайте посмотрим на пример:
val city: String? = "Kolkata" return if (city != null) { city.length } else { null }
Оператор безопасного вызова (?.)
В Kotlin есть безопасный оператор вызова (?.) для обработки пустых ссылок. Этот оператор выполняет любое действие только тогда, когда ссылка имеет ненулевое значение. В противном случае возвращается нулевое значение. Оператор безопасного вызова объединяет проверку на null и вызов метода в одном выражении.
Давайте посмотрим, как использовать безопасный оператор вызова:
val country: String? = "India" assertEquals(5, country?.length) val city: String? = null assertNull(city?.length)
Мы также можем использовать безопасный оператор вызова для нескольких цепных вызовов:
val country: Country? = Country(City("Kolkata", "003")) val code: String? = country?.city?.code assertEquals("003", code)
Вызовы цепочки возвращают значение null, если какое-либо из свойств имеет значение null:
val country: Country? = Country(null) val code: String? = country?.city?.code assertNull(code)
Использование метода let ()
Мы можем использовать метод let () вместе с оператором безопасного вызова, чтобы воздействовать на переменную, не допускающую значения NULL:
val cities: List<String?> = listOf("Kolkata", null, "Mumbai") var name: List<String?> = emptyList() for (city in cities) { city?.let { name = name.plus(it) } } return name assertEquals(2, name.size)
Использование метода also ()
Мы можем использовать метод also () для выполнения дополнительных операций, таких как регистрация и печать переменных, не допускающих значения NULL. Этот метод можно использовать в цепочке с методом let () или run ().
Вот как мы можем использовать метод also () вместе с методом let ():
val cities: List<String?> = listOf("Kolkata", null, "Mumbai") var name: List<String?> = emptyList() for (city in cities) { city?.let { name = name.plus(it) it }?.also { println("Logging the value: $it") } } return name assertEquals(2, name.size)
Использование метода run ()
Мы можем использовать метод run () для выполнения некоторых операций со ссылкой, не допускающей значения NULL. Этот метод работает с использованием ссылки this и возвращает значение результата лямбда:
val countries: List<String?> = listOf("India", null, "Germany") var name: List<String?> = emptyList() for (country in countries) { country?.run { name = name.plus(this) this }?.also { println("Logging the value: $it") } } return name assertEquals(2, name.size)
Оператор Элвиса (?:)
Мы можем использовать оператор Элвиса (? :) для возврата значения по умолчанию, только если исходная переменная имеет нулевое значение. Если левое выражение оператора Элвиса имеет значение, не допускающее обнуления, оно возвращается. В противном случае возвращается правое выражение.
Давайте посмотрим, как работает оператор Элвиса:
val country: Country? = Country(City("New Delhi", null)) val result = country?.city?.code ?: "Not available" assertEquals("Not available", result)
Мы можем использовать оператор E lvis с оператором безопасного вызова для вызова метода или свойства переменной:
val country: Country? = Country(City("Mumbai", "002")) val result = country?.city?.code ?: "Not available" assertEquals("002", result)
Мы также можем использовать выражение throw и return в правом выражении операции Элвиса r. Таким образом, вместо значений по умолчанию мы можем выдавать определенные исключения в правых выражениях оператора Элвиса:
val country: Country? = Country(City("Chennai", null)) val result = country?.city?.code ?: throw IllegalArgumentException("Not a valid code") assertThrows<IllegalArgumentException> { result }
Оператор утверждения ненулевого значения (!!)
Мы можем использовать оператор утверждения ненулевого значения (!!), чтобы явно выбросить NullPointerException. Этот оператор преобразует любую ссылку в свой тип, не допускающий значения NULL, и выдает исключение, если ссылка имеет значение NULL.
Давайте посмотрим, как мы можем выбросить NullPointerException, используя оператор утверждения not-null (!!):
val country: String? = null val result : Int = country!!.length assertThrows<NullPointerException> { result }
Однако, если ссылка имеет значение, не допускающее значения NULL, она выполняется успешно:
val country: String? = "India" val result : Int = country!!.length assertEquals(5, result)
Оператор утверждения not-null следует использовать осторожно, поскольку он является потенциальным признаком NullPointerException. Мы должны избегать использования нескольких ненулевых утверждений, подобных приведенному ниже, поскольку это затрудняет отладку того, какое свойство имеет значение null:
country!!.city!!.code
В таких случаях мы всегда должны стараться использовать безопасный оператор вызова, чтобы исключить NullPointerException:
country?.city?.code
Возможность обнуления в коллекциях
По умолчанию коллекции Kotlin не допускают значения NULL. Чтобы определить коллекцию обнуляемых типов в Kotlin, мы должны добавить вопросительный знак (?) К объявлению типа:
val countries: List<String?> = listOf("India", null, "Germany", "Russia", null)
Мы можем использовать следующий способ определения коллекции, допускающей значение NULL, в Kotlin:
var countries: List<String>? = listOf("India", "Germany", "Russia") countries = null
Фильтрация типов, допускающих значение NULL
Мы можем отфильтровать список, содержащий значения, допускающие значение NULL, чтобы вернуть только значения, не допускающие значения NULL, с помощью метода filterNotNull ().
Давайте посмотрим на пример:
val countries: List<String?> = listOf("India", null, "Germany", "Russia", null) val result: List<String> = countries.filterNotNull() assertEquals(3, result.size) assertEquals("Russia", result[2])
Заключение
В этой статье мы рассмотрим различные способы обработки ссылок, допускающих значение NULL, в Kotlin.
Код этих примеров доступен на GitHub.
Первоначально опубликовано на https://expatdev.com