Найкращий спосіб нульової перевірки в Котліні?


97

Чи слід використовувати подвійний =чи потрійний =?

if(a === null)  {
//do something
}

або

if(a == null)  {
//do something
}

Аналогічно для "не дорівнює":

if(a !== null)  {
//do something
}

або

if(a != null)  {
//do something
}

1
подивіться на посилання: - kotlinlang.org/docs/reference/null-safety.html .............. Це легко в Документах Kotlin
sushildlh

Відповіді:


62

Обидва підходи генерують однаковий байт-код, щоб ви могли вибрати все, що вам подобається.


4
Якщо я правильно зрозумів, тоді він просить найкращого способу перевірити нуль у Kotlin, а не який підхід генерує найкращий байт-код. @ BenitoBertoli відповідь виглядає багатообіцяючою, це зменшує шаблонний код
imGs

155

Структурна рівність a == bперекладається на

a?.equals(b) ?: (b === null)

Тому при порівнянні з null, структурна рівність a == nullперекладається на референтну рівність a === null.

Згідно з документами , немає сенсу оптимізувати ваш код, тому ви можете використовувати a == nullта a != null


зауважте, що якщо змінна є властивістю, що змінюється, ви не зможете розумно передати її до її ненульованого типу всередині ifоператора (оскільки значення могло бути змінено іншим потоком), і letзамість цього вам доведеться використовувати оператор безпечного виклику .

Безпечний дзвінок оператора ?.

a?.let {
   // not null do something
   println(it)
   println("not null")
}


Ви можете використовувати його в поєднанні з оператором Elvis.

Оператор Елвіса ?: (я здогадуюсь, бо знак допиту схожий на волосся Елвіса)

a ?: println("null")

І якщо ви хочете запустити блок коду

a ?: run {
    println("null")
    println("The King has left the building")
}

Поєднання двох

a?.let {
   println("not null")
   println("Wop-bop-a-loom-a-boom-bam-boom")
} ?: run {
    println("null")
    println("When things go null, don't go with them")
}

1
чому ви не використовуєте ifдля нульових перевірок? a?.let{} ?: run{}доречний лише в рідкісних випадках, інакше це не ідіоматично
воддан

2
@voddan Я не пропонував не використовувати якщо для nullперевірок, я перелічував інші життєздатні варіанти. Хоча я не впевнений, чи runпередбачено якесь покарання за продуктивність. Я оновлю свою відповідь, щоб зробити її більш зрозумілою.
Беніто Бертолі,

1
@voddan Якщо aє var, то, використовуючи a?.let{} ?: run{}гарантію, що він буде належним чином пов'язаний у letвсьому обсязі. Якщо aє a val, то різниці немає.
madeinqc

1
@madeinqc якщо a - це val, то використання let - це інше, і це погано. Я знайшов цю статтю дуже вдалою для її пояснення - Котлін: Не використовуйте просто LET для нульової перевірки .
Суфіан

38

Котлінські способи поводження з нулем

Операція безпечного доступу

val dialog : Dialog? = Dialog()
dialog?.dismiss()  // if the dialog will be null,the dismiss call will be omitted

Нехай функціонує

user?.let {
  //Work with non-null user
  handleNonNullUser(user)
}

Дочасний вихід

fun handleUser(user : User?) {
  user ?: return //exit the function if user is null
  //Now the compiler knows user is non-null
}

Незмінні тіні

var user : User? = null

fun handleUser() {
  val user = user ?: return //Return if null, otherwise create immutable shadow
  //Work with a local, non-null variable named user
}

Значення за замовчуванням

fun getUserName(): String {
 //If our nullable reference is not null, use it, otherwise use non-null value 
 return userName ?: "Anonymous"
}

Використовуйте val замість var

valє лише для читання, varє змінним. Рекомендується використовувати якомога більше властивостей лише для читання, вони безпечні для потоку.

Використовуйте латеніт

Іноді не можна використовувати незмінні властивості. Наприклад, це відбувається на Android, коли якесь властивість ініціалізується у onCreate()виклику. У цих ситуаціях Котлін має мовну функцію, яка називається lateinit.

private lateinit var mAdapter: RecyclerAdapter<Transaction>

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   mAdapter = RecyclerAdapter(R.layout.item_transaction)
}

fun updateTransactions() {
   mAdapter.notifyDataSetChanged()
}

Я б назвав останнє "значенням за замовчуванням" (не elvis), оскільки 3/4 з них використовують elvis.
AjahnCharles

@AjahnCharles має сенс))
Левон Петросян

1
це сміття, будь-яка сучасна мова може мати краще справу з іншими, ніж ця. це більше звична робота, ніж користь для програмістів.
JBarros35,

10

Додаток до @Benito Bertoli,

комбінація насправді не схожа на if-else

"test" ?. let {
    println ( "1. it=$it" )
} ?: let {
    println ( "2. it is null!" )
}

Результат:

1. it=test

Але якщо:

"test" ?. let {
    println ( "1. it=$it" )
    null // finally returns null
} ?: let {
    println ( "2. it is null!" )
}

Результат:

1. it=test
2. it is null!

Крім того, якщо спочатку використовувати Elvis:

null ?: let {
    println ( "1. it is null!" )
} ?. let {
    println ( "2. it=$it" )
}

Результат:

1. it is null!
2. it=kotlin.Unit

5

Перевірте корисні методи, це може бути корисно:

/**
 * Performs [R] when [T] is not null. Block [R] will have context of [T]
 */
inline fun <T : Any, R> ifNotNull(input: T?, callback: (T) -> R): R? {
    return input?.let(callback)
}

/**
 * Checking if [T] is not `null` and if its function completes or satisfies to some condition.
 */
inline fun <T: Any> T?.isNotNullAndSatisfies(check: T.() -> Boolean?): Boolean{
    return ifNotNull(this) { it.run(check) } ?: false
}

Нижче наведено приклад використання цих функцій:

var s: String? = null

// ...

if (s.isNotNullAndSatisfies{ isEmpty() }{
   // do something
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.