1. Обзор
В этой статье мы рассмотрим нулевые функции безопасности, встроенные в язык Kotlin. Kotlin обеспечивает комплексную встроенную обработку полей, допускающих значение NULL, — дополнительные библиотеки не требуются.
2. Зависимость Maven
Чтобы начать, вам нужно добавить зависимость kotlin-stdlib Maven в ваш pom.xml:
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>1.1.1</version>
</dependency>
3. Ссылочные типы, допускающие и не допускающие null
В Kotlin есть два типа ссылок , которые интерпретируются компилятором, чтобы предоставить программисту информацию о корректности программы во время компиляции: те, которые допускают значение NULL, и те, которые нет.
По умолчанию Котлин предполагает, что значение не может быть нулевым:
var a: String = "value"
assertEquals(a.length, 5)
Мы не можем присвоить значение null ссылке a , и если вы попытаетесь это сделать, это вызовет ошибку компилятора.
Если мы хотим создать ссылку, допускающую значение NULL, нам нужно добавить вопросительный знак (?) к определению типа:
var b: String? = "value"
После этого мы можем присвоить ему значение null:
b = null
Когда мы хотим получить доступ к ссылке b , мы должны явно обрабатывать случай null , чтобы избежать ошибки компиляции, поскольку Kotlin знает, что эта переменная может содержать null:
if (b != null) {
println(b.length)
} else {
assertNull(b)
}
4. Безопасные звонки
Подобная обработка каждой ссылки, допускающей значение NULL, может быть обременительной. К счастью, в Kotlin есть синтаксис для «безопасных вызовов» — этот синтаксис позволяет программистам выполнять действие только тогда, когда конкретная ссылка содержит ненулевое значение.
Давайте определим два класса данных, чтобы проиллюстрировать эту функцию:
data class Person(val country: Country?)
data class Country(val code: String?)
Обратите внимание, что поля страны и кода имеют ссылочный тип, допускающий значение NULL.
Чтобы обеспечить свободный доступ к этим полям, мы можем использовать синтаксис безопасного вызова:
val p: Person? = Person(Country("ENG"))
val res = p?.country?.code
assertEquals(res, "ENG")
Если переменная p содержит значение null , синтаксис безопасных вызовов вернет нулевой результат:
val p: Person? = Person(Country(null))
val res = p?.country?.code
assertNull(res)
4.1. Метод let()
Чтобы выполнить действие только тогда, когда ссылка содержит значение, не допускающее значения NULL, мы можем использовать оператор let.
Допустим, у нас есть список значений, и в этом списке также есть нулевое значение:
val firstName = "Tom"
val secondName = "Michael"
val names: List<String?> = listOf(firstName, null, secondName)
Далее мы можем выполнить действие над каждым необнуляемым элементом списка имен , используя функцию let:
var res = listOf<String?>()
for (item in names) {
item?.let { res = res.plus(it) }
}
assertEquals(2, res.size)
assertTrue { res.contains(firstName) }
assertTrue { res.contains(secondName) }
4.2. Метод also()
Если мы хотим применить какую-то дополнительную операцию, например, регистрацию каждого значения, не допускающего значения NULL, мы можем использовать метод также() и связать его с помощью let():
var res = listOf<String?>()
for (item in names) {
item?.let { res = res.plus(it); it }
?.also{it -> println("non nullable value: $it")}
}
Он распечатает каждый элемент, который не равен нулю:
non nullable value: Tom
non nullable value: Michael
4.3. Метод run()
В Котлине есть метод run() для выполнения некоторой операции над ссылкой, допускающей значение NULL. Он очень похож на let() , но внутри тела функции метод Run() работает с этой ссылкой, а не с параметром функции:
var res = listOf<String?>()
for (item in names) {
item?.run{res = res.plus(this)}
}
5. Elvis оператор
Иногда, когда у нас есть ссылка, мы хотим вернуть из операции какое-то значение по умолчанию, если ссылка содержит значение null . Для этого мы можем использовать оператор Элвиса ( ?: ). Это эквивалент orElse/orElseGet из дополнительного класса Java:
val value: String? = null
val res = value?.length ?: -1
assertEquals(res, -1)
Когда ссылка на значение содержит значение, не допускающее значения NULL, будет вызван метод length:
val value: String? = "name"
val res = value?.length ?: -1
assertEquals(res, 4)
6. Обнуляемый небезопасный метод Get
В Котлине также есть небезопасный оператор для получения значения поля, допускающего значение NULL, без явной обработки логики отсутствия, но его следует использовать очень осторожно.
Оператор двойного восклицательного знака ( !! ) принимает значение из ссылки, допускающей значение NULL, и генерирует исключение NullPointerException, если оно содержит значение NULL. Это эквивалент операцииOptional.get():
var b: String? = "value"
b = null
assertFailsWith<NullPointerException> {
b!!
}
Если ссылка, допускающая значение NULL, содержит значение, не допускающее NULL, действие над этим значением будет выполнено успешно:
val b: String? = "value"
assertEquals(b!!.length, 5)
7. Фильтрация нулевых значений из списка
Класс List в Kotlin имеет служебный метод filterNotNull() , который возвращает только значения, не допускающие значения NULL, из списка, содержащего ссылки, допускающие значение NULL:
val list: List<String?> = listOf("a", null, "b")
val res = list.filterNotNull()
assertEquals(res.size, 2)
assertTrue { res.contains("a") }
assertTrue { res.contains("b") }
Это очень полезная конструкция, инкапсулирующая логику, которую в противном случае нам пришлось бы реализовать самостоятельно.
8. Заключение
В этой статье мы подробно изучили нулевые функции безопасности Колтина. Мы видели типы ссылок, которые могут содержать нулевые значения, и те, которые не могут. Мы реализовали плавную логику обработки нулей , используя функции «безопасного вызова» и оператор elvis.
Оригинал статьи: https://www.baeldung.com/kotlin/null-safety