Intereting Posts
Заголовки HTTP не возвращаются на EC2 Anko: внутренняя область применения, сбивающее с толку разрешение собственности Геттеры и сеттеры в Котлине Флажок в Recycler Элемент View, если установлен в true, проверяется на наличие ложных срабатываний Переопределяющая переменная создает NPE, когда переменная используется при инициализации в суперклассе Kotlin: Как можно получить доступ к функциям расширения внутри одного типа извне? Почему мы не можем публиковать типы типов из частных расширенных типов? Тесты инструментария не удалось из-за NoClassDefFoundError Что означает «им» в Котлине? Проблема с помехами в WebFlux WebTestClient и Kotlin Как объединить план и предыдущий Single Зависимости Bintray не будут загружаться в InteliJ Есть ли способ повторно использовать экземпляр задания? Свойство Kotlin: «Тип параметра свойства должен использоваться в его типе приемника» Как я могу вызвать collect (Collectors.toList ()) в потоке Java 8 в Котлине?

В Котлине, какой идиоматический способ справиться с значениями NULL, ссылаться или преобразовывать их

Если у меня есть Xyz? тип Xyz? , Я хочу ссылаться на него или преобразовать его в Xyz тип Xyz . Каков идиоматический способ сделать это в Котлине?

Например, этот код является ошибкой:

 val something: Xyz? = createPossiblyNullXyz() something.foo() // Error: "Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Xyz?" 

Но если я сначала проверю null, это допустимо, почему?

 val something: Xyz? = createPossiblyNullXyz() if (something != null) { something.foo() } 

Как изменить или обработать значение как не равное null не требуя проверки if , предполагая, что я точно знаю, что оно действительно никогда не является null ? Например, здесь я получаю значение с карты, которую я могу гарантировать, и результат get() не равен null . Но у меня есть ошибка:

 val map = mapOf("a" to 65,"b" to 66,"c" to 67) val something = map.get("a") something.toLong() // Error: "Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Int?" 

Метод get() считает возможным, что элемент отсутствует и возвращает тип Int? , Таким образом, что является лучшим способом заставить тип значения быть недействительным?

Примечание: этот вопрос намеренно написан автором и автору ( ответы на вопросы ), так что идиоматические ответы на часто задаваемые темы Котлина присутствуют в SO. Также прояснить некоторые действительно старые ответы, написанные для альфов Котлина, которые не точны для сегодняшнего дня Котлин.

Solutions Collecting From Web of "В Котлине, какой идиоматический способ справиться с значениями NULL, ссылаться или преобразовывать их"

Во-первых, вы должны прочитать все о Null Safety в Котлине, который тщательно охватывает дела.

В Kotlin вы не можете получить доступ к значению с null значением, не будучи уверенным, что оно не является null ( проверка на null в условиях ) или утверждение, что оно, безусловно, не является null используя !! уверенного оператора , обращаясь к нему с помощью ?. Safe Call или, наконец, дать что-то, что может быть null значением по умолчанию, используя оператор ?: Elvis .

Для вашего 1-го случая в вашем вопросе у вас есть варианты в зависимости от намерения кода, который вы бы использовали одним из них, и все они идиоматичны, но имеют разные результаты:

 val something: Xyz? = createPossiblyNullXyz() // access it as non-null asserting that with a sure call val result1 = something!!.foo() // access it only if it is not null using safe operator, // returning null otherwise val result2 = something?.foo() // access it only if it is not null using safe operator, // otherwise a default value using the elvis operator val result3 = something?.foo() ?: differentValue // null check it with `if` expression and then use the value, // similar to result3 but for more complex cases harder to do in one expression val result4 = if (something != null) { something.foo() } else { ... differentValue } // null check it with `if` statement doing a different action if (something != null) { something.foo() } else { someOtherAction() } 

Для «Почему это работает при нулевом проверке» прочитайте приведенную ниже справочную информацию о умных передачах .

Для вашего 2-го случая в вашем вопросе в вопросе с Map , если вы как разработчик уверены в том, что результат никогда не был null , используйте !! верный оператор в качестве утверждения:

 val map = mapOf("a" to 65,"b" to 66,"c" to 67) val something = map.get("a")!! something.toLong() // now valid 

или в другом случае, когда карта COULD возвращает нуль, но вы можете getOrElse значение по умолчанию, то сама Map имеет метод getOrElse :

 val map = mapOf("a" to 65,"b" to 66,"c" to 67) val something = map.getOrElse("z") { 0 } // provide default value in lambda something.toLong() // now valid 

Исходная информация:

Примечание. В приведенных ниже примерах я использую явные типы, чтобы сделать поведение понятным. С типом вывода обычно типы могут быть опущены для локальных переменных и частных членов.

Подробнее о !! уверенный оператор

!! оператор утверждает, что значение не равно null или выбрасывает NPE. Это должно использоваться в тех случаях, когда разработчик гарантирует, что значение никогда не будет равно null . Подумайте об этом как о утверждении, за которым следует умный актерский состав .

 val possibleXyz: Xyz? = ... // assert it is not null, but if it is throw an exception: val surelyXyz: Xyz = possibleXyz!! // same thing but access members after the assertion is made: possibleXyz!!.foo() 

читать дальше: !! Уверенный оператор


Подробнее о null Checking и Smart Casts

Если вы защитите доступ к типу с null типом с null проверкой, компилятор будет считать, что значение внутри тела оператора не имеет значения NULL. Есть некоторые сложные потоки, когда этого не может быть, но для обычных случаев работает нормально.

 val possibleXyz: Xyz? = ... if (possibleXyz != null) { // allowed to reference members: possiblyXyz.foo() // or also assign as non-nullable type: val surelyXyz: Xyz = possibleXyz } 

Или, если вы делаете a, is проверка типа с нулевым значением:

 if (possibleXyz is Xyz) { // allowed to reference members: possiblyXyz.foo() } 

И то же самое для выражений «when», которые также безопасны:

 when (possibleXyz) { null -> doSomething() else -> possibleXyz.foo() } // or when (possibleXyz) { is Xyz -> possibleXyz.foo() is Alpha -> possibleXyz.dominate() is Fish -> possibleXyz.swim() } 

Некоторые вещи не позволяют использовать null проверку для умного приведения для последующего использования переменной. В приведенном выше примере используется локальная переменная, которая никоим образом не могла быть мутирована в потоке приложения, независимо от того, была ли val или var этой переменной не иметь мутаций в null . Но в других случаях, когда компилятор не может гарантировать анализ потока, это будет ошибкой:

 var nullableInt: Int? = ... public fun foo() { if (nullableInt != null) { // Error: "Smart cast to 'kotlin.Int' is impossible, because 'nullableInt' is a mutable property that could have been changed by this time" val nonNullableInt: Int = nullableInt } } 

Жизненный цикл переменной nullableInt не полностью виден и может быть назначен из других потоков, null проверка не может быть умной, отлитой в значение, не равное null . См. Раздел «Безопасные вызовы» ниже для обходного пути.

Другим случаем, которому нельзя доверять умный бросок, чтобы не мутировать, является свойство val для объекта, у которого есть пользовательский getter. В этом случае компилятор не имеет видимости того, что изменяет значение, и поэтому вы получите сообщение об ошибке:

 class MyThing { val possibleXyz: Xyz? get() { ... } } // now when referencing this class... val thing = MyThing() if (thing.possibleXyz != null) { // error: "Kotlin: Smart cast to 'kotlin.Int' is impossible, because 'px' is a property that has open or custom getter" thing.possiblyXyz.foo() } 

подробнее: Проверка нулевого условия


Подробнее о ?. Оператор Safe Call

Оператор безопасного вызова возвращает null, если значение слева имеет значение null, в противном случае продолжает оценивать выражение справа.

 val possibleXyz: Xyz? = makeMeSomethingButMaybeNullable() // "answer" will be null if any step of the chain is null val answer = possibleXyz?.foo()?.goo()?.boo() 

Еще один пример, когда вы хотите перебирать список, но только если он не является null и не пустым, снова может понадобиться оператор безопасного вызова:

 val things: List? = makeMeAListOrDont() things?.forEach { // this loops only if not null (due to safe call) nor empty (0 items loop 0 times): } 

В одном из приведенных выше примеров у нас был случай, когда мы выполнили проверку if но имели шанс, что другой поток изменил значение и, следовательно, не стал умным . Мы можем изменить этот образец, чтобы использовать оператор безопасного вызова вместе с функцией let чтобы решить эту проблему:

 var possibleXyz: Xyz? = 1 public fun foo() { possibleXyz?.let { value -> // only called if not null, and the value is captured by the lambda val surelyXyz: Xyz = value } } 

подробнее: Безопасные звонки


Подробнее об этом ?: Elvis Operator

Оператор Elvis позволяет вам предоставить альтернативное значение, когда выражение слева от оператора имеет значение null :

 val surelyXyz: Xyz = makeXyzOrNull() ?: DefaultXyz() 

Он также использует некоторые виды творческого использования, например, выдает исключение, когда что-то имеет значение null :

 val currentUser = session.user ?: throw Http401Error("Unauthorized") 

или вернуться раньше от функции:

 fun foo(key: String): Int { val startingCode: String = codes.findKey(key) ?: return 0 // ... return endingValue } 

подробнее: Оператор Elvis


Нулевые операторы со связанными функциями

Kotlin stdlib имеет ряд функций, которые действительно работают с операторами, упомянутыми выше. Например:

 // use ?.let() to change a not null value, and ?: to provide a default val something = possibleNull?.let { it.transform() } ?: defaultSomething // use ?.apply() to operate further on a value that is not null possibleNull?.apply { func1() func2() } // use .takeIf or .takeUnless to turn a value null if it meets a predicate val something = name.takeIf { it.isNotBlank() } ?: defaultName val something = name.takeUnless { it.isBlank() } ?: defaultName 

Похожие темы

В Kotlin большинство приложений стараются избегать null значений, но это не всегда возможно. И иногда null имеет смысл. Некоторые рекомендации:

  • в некоторых случаях он гарантирует разные типы возврата, которые включают статус вызова метода и результат в случае успеха. Библиотеки, такие как « Результат», дают вам результат успеха или результат сбоя, который также может разветвлять ваш код. И библиотека обещаний для Котлина, называемая Ковеном, делает то же самое в виде обещаний.

  • для коллекций как возвращаемые типы всегда возвращают пустую коллекцию вместо null , если вам не требуется третье состояние «нет». Для создания этих пустых значений у Kotlin есть вспомогательные функции, такие как emptyList() или emptySet() .

  • при использовании методов, возвращающих значение nullable, для которого у вас есть значение по умолчанию или альтернатива, используйте оператор Elvis для предоставления значения по умолчанию. В случае использования Map используйте getOrElse() который позволяет генерировать значение по умолчанию вместо метода Map get() который возвращает значение с нулевым значением. То же самое для getOrPut()

  • при переопределении методов с Java, где Kotlin не уверен в нулевости кода Java, вы всегда можете отказаться ? nullability от вашего переопределения, если вы уверены, что должна быть подпись и функциональность. Поэтому ваш переопределенный метод более безопасен. То же самое для реализации интерфейсов Java в Котлине, изменить значение nullability, чтобы быть то, что вы знаете, является допустимым.

  • посмотрите на функции, которые могут помочь уже, например, для String?.isNullOrEmpty() и String?.isNullOrBlank() которые могут безопасно работать с нулевым значением и делать то, что вы ожидаете. Фактически вы можете добавить свои собственные расширения, чтобы заполнить пробелы в стандартной библиотеке.

  • такие как checkNotNull() и requireNotNull() в стандартной библиотеке.

  • вспомогательные функции, такие как filterNotNull() которые удаляют нули из коллекций или listOfNotNull() для возврата списка нулей или отдельных элементов из возможного null значения.

  • существует также и оператор (исключаемый) с безопасностью (nullable), который допускает, чтобы приведение к типу с нулевым значением возвращало значение null, если это невозможно. Но у меня нет подходящего варианта для этого, который не решается другими способами, упомянутыми выше.

Предыдущий ответ – это тяжелый поступок, но вот один из быстрых и простых способов:

 val something: Xyz = createPossiblyNullXyz() ?: throw RuntimeError("no it shouldn't be null") something.foo() 

Если это действительно никогда не является нулевым, исключение не произойдет, но если это когда-нибудь, вы увидите, что пошло не так.