Почему я вынужден использовать !! в сочетании с нулевыми проверками?

Для этой части кода Котлин заставляет меня обрабатывать ситуацию с нулевой проверкой, а на другой линии это не так, несмотря на то, что она такая же:

if (r1 == null && r2 == null) throw IllegalArgumentException("All nulls!") else if (r1 == null) return r2!!.reading // <----- I am forced to !! here else if (r2 == null) return r1.reading // <----- The compiler does not complain in this line 

Это ошибка или функция?

Компилятор Kotlin не делает логических выводов так, как вы ожидаете: «Я уже проверил, что r1 == null и r2 == null, так что теперь, если я проверяю только r1 == null, тогда он должен признать, что r2 не является нулевым ". Это не так. Вы не проверили r2 в этой ветви, поэтому он не видит, что он не является нулевым.

Во втором случае ситуация проще: if (r1 == null) { ... } else { ... } . Не имеет значения, что у вас есть еще одна проверка внутри else ; компилятор видит, что вы находитесь в ветке else if (x == null) и понимаете, что значение не равно null.

Существует открытый запрос функции для добавления такой логики, однако это не на ближайшую дорожную карту команды Kotlin.

IMHO, если некоторые логики приложений являются взаимоисключающими. Я бы предпочел использовать, when {} декларативно выражать полные условия в каждом случае, а не использовать if(...) else if(..) else чтобы составить логику.

Использование, when{} чтобы указать полные условия в каждом случае, может помочь разработчикам просто сосредоточиться на конкретном случае, который проще для отладки и последующих изменений.

Напротив, условия if else заставляли разработчиков отслеживать сверху вниз, чтобы получить всю идею этой частичной логики, которая довольно трудно читать другим и трудно указать на недостижимый код.

Я встречал некоторые сложные коды if else и мне нужно построить таблицу истинности для понимания всей части логики.

Возвращаясь к вопросу. Смарт-футляр хорошо работает, when{} 🙂

Реализация 1

 val someReading = when { r1 != null && r2 == null -> r1.reading r1 == null && r2 != null -> r2.reading else -> throw IllegalArgumentException("All nulls!") } 

Реализация 2

 val someReading = r1?.reading ?: r2?.reading ?: throw IllegalArgumentException("All nulls!") 

Вы можете слегка перестроить выражение if , чтобы оно делало меньше проверок и применяло smart-cast к r2 :

 if (r1 == null) { if (r2 == null) throw IllegalArgumentException("All nulls!") return r2.reading } else if (r2 == null) { return r1.reading }