Najlepszy sposób na zerową weryfikację w Kotlinie?

98

Powinienem użyć podwójnego =czy potrójnego =?

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

lub

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

Podobnie dla „nie równa się”:

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

lub

if(a != null)  {
//do something
}
pdeva
źródło
1
spójrz na link: - kotlinlang.org/docs/reference/null-safety.html .............. To łatwe w Kotlin Docs
sushildlh

Odpowiedzi:

62

Obie metody generują ten sam kod bajtowy, więc możesz wybrać, co wolisz.

Michał
źródło
4
Jeśli dobrze to zrozumiałem, to pyta o najlepszy sposób sprawdzenia null w Kotlinie, a nie które podejście generuje najlepszy kod bajtowy. @ BenitoBertoli odpowiedź wygląda obiecująco, zmniejsza standardowy kod
imGs
155

Równość strukturalna a == bjest tłumaczona na

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

Dlatego w porównaniu nullz równością strukturalną a == nullprzekłada się na równość referencyjną a === null.

Zgodnie z dokumentacją nie ma sensu optymalizowanie kodu, więc możesz użyć a == nulli a != null


Zauważ, że jeśli zmienna jest zmienną właściwością, nie będziesz w stanie inteligentnie rzutować jej na jej typ, który nie dopuszcza wartości null w ifinstrukcji (ponieważ wartość mogła zostać zmodyfikowana przez inny wątek) i letzamiast tego musiałbyś użyć bezpiecznego operatora wywołania .

Bezpieczny operator połączeń ?.

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


Możesz go używać w połączeniu z operatorem Elvisa.

Operator Elvisa ?: (zgaduję, ponieważ znak przesłuchania wygląda jak włosy Elvisa)

a ?: println("null")

A jeśli chcesz uruchomić blok kodu

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

Połączenie tych dwóch

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")
}
Benito Bertoli
źródło
1
dlaczego nie używasz ifdo sprawdzania wartości zerowej? a?.let{} ?: run{}jest właściwe tylko w rzadkich przypadkach, w przeciwnym razie nie jest idiomatyczne
voddan
2
@voddan Nie sugerowałem, aby nie używać if do nullkontroli, wymieniłem inne możliwe opcje. Chociaż nie jestem pewien, czy runma jakiś rodzaj spadku wydajności. Zaktualizuję odpowiedź, aby była bardziej przejrzysta.
Benito Bertoli
1
@voddan Jeśli ajest a var, to korzystając z a?.let{} ?: run{}gwarancji, że zostanie on poprawnie powiązany w letcałym zakresie. Jeśli ajest val, to nie ma różnicy.
madeinqc
1
@madeinqc jeśli a jest a val, to użycie let jest inne i złe. Uważam, że ten artykuł jest bardzo dobry w wyjaśnianiu tego - Kotlin: Nie używaj LET tylko do sprawdzenia zerowego .
Sufian
39

Kotlin sposoby obsługi null

Bezpieczna operacja dostępu

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

Niech funkcjonuje

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

Wczesne wyjście

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

Niezmienne cienie

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
}

Domyślna wartość

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

Użyj val zamiast var

valjest tylko do odczytu, varjest zmienny. Zaleca się używanie jak największej liczby właściwości tylko do odczytu, są one bezpieczne dla wątków.

Użyj lateinit

Czasami nie można użyć niezmiennych właściwości. Na przykład dzieje się tak w systemie Android, gdy w onCreate()wywołaniu inicjowana jest jakaś właściwość . W takich sytuacjach Kotlin ma funkcję języka o nazwie 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()
}
Levon Petrosyan
źródło
Ostatnią z nich nazwałbym „wartością domyślną” (nie elvis), ponieważ 3/4 z nich używa elvisa.
AjahnCharles
@AjahnCharles ma sens))
Levon Petrosyan
1
to bzdura, każdy nowoczesny język radzi sobie lepiej z opcjami niż ten. to bardziej uciążliwy obowiązek niż korzyść dla programistów.
JBarros35
10

Dodatek do @Benito Bertoli,

kombinacja jest w rzeczywistości odmienna od if-else

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

Wynik to:

1. it=test

Ale jeśli:

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

Wynik to:

1. it=test
2. it is null!

Ponadto, jeśli najpierw użyjesz elvisa:

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

Wynik to:

1. it is null!
2. it=kotlin.Unit
BingLi224
źródło
5

Sprawdź przydatne metody, może się przydać:

/**
 * 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
}

Poniżej możliwy przykład wykorzystania tych funkcji:

var s: String? = null

// ...

if (s.isNotNullAndSatisfies{ isEmpty() }{
   // do something
}
Vlad
źródło