Найти тему

Комплексное руководство по нулевой безопасности в Kotlin

Оглавление

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