Melhor maneira de anular a verificação em Kotlin?

94

Devo usar duplo =ou triplo =?

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

ou

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

Da mesma forma para 'diferente de':

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

ou

if(a != null)  {
//do something
}
PDeva
fonte
1
dê uma olhada no link: - kotlinlang.org/docs/reference/null-safety.html .............. É fácil no Kotlin Docs
sushildlh

Respostas:

62

Ambas as abordagens geram o mesmo bytecode para que você possa escolher o que preferir.

Michael
fonte
2
Se entendi corretamente, ele está perguntando qual é a melhor maneira de verificar nulos em Kotlin, e não qual abordagem gera o melhor código de bytes. @BenitoBertoli a resposta parece promissora, reduz o código clichê
imGs
148

Uma igualdade estrutural a == bé traduzida em

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

Portanto, ao comparar com null, a igualdade estrutural a == nullé traduzida em uma igualdade referencial a === null.

De acordo com os documentos , não há nenhum ponto em otimizar seu código, então você pode usar a == nulle a != null


Observe que se a variável for uma propriedade mutável, você não será capaz de fazer um smart cast para seu tipo não anulável dentro da ifinstrução (porque o valor pode ter sido modificado por outro encadeamento) e você terá que usar o operador de chamada segura com let.

Operadora de chamada segura ?.

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


Você pode usá-lo em combinação com o operador Elvis.

Operador de Elvis ?: (suponho que seja porque a marca de interrogatório se parece com o cabelo de Elvis)

a ?: println("null")

E se você deseja executar um bloco de código

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

Combinando os dois

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
fonte
1
por que você não usa ifpara verificações nulas? a?.let{} ?: run{}só é apropriado em casos raros, caso contrário, não é idiomático
voddan
2
@voddan Eu não estava sugerindo não usar if para nullcheques, eu estava listando outras opções viáveis. Embora eu não tenha certeza se runtem algum tipo de penalidade de desempenho. Vou atualizar minha resposta para torná-la mais clara.
Benito Bertoli
1
@voddan Se afor um var, use a a?.let{} ?: run{}garantia de que será vinculado corretamente no letpara todo o escopo. Se afor um val, então não há diferença.
madeinqc
1
@madeinqc se a for um val, então usar let é diferente e é ruim. Achei este artigo muito bom em explicar isso - Kotlin: Não use LET apenas para verificação de nulos .
Sufian
34

Maneiras Kotlin de lidar com nulos

Operação de acesso seguro

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

Deixe funcionar

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

Saída antecipada

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

Sombras imutáveis

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
}

Valor padrão

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

Use val em vez de var

valé somente leitura, varé mutável. Recomenda-se usar tantas propriedades somente leitura quanto possível, elas são thread-safe.

Use lateinit

Às vezes, você não pode usar propriedades imutáveis. Por exemplo, isso acontece no Android quando alguma propriedade é inicializada na onCreate()chamada. Para essas situações, o Kotlin possui um recurso de linguagem chamado 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
fonte
Eu chamaria o último de "valor padrão" (não elvis), já que 3/4 deles estão usando o elvis.
AjahnCharles de
@AjahnCharles faz sentido))
Levon Petrosyan
8

Adição a @Benito Bertoli,

a combinação é realmente diferente de if-else

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

O resultado é:

1. it=test

Mas se:

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

O resultado é:

1. it=test
2. it is null!

Além disso, se usar o Elvis primeiro:

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

O resultado é:

1. it is null!
2. it=kotlin.Unit
BingLi224
fonte
5

Verifique métodos úteis, pode ser útil:

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

Abaixo está um exemplo possível de como usar essas funções:

var s: String? = null

// ...

if (s.isNotNullAndSatisfies{ isEmpty() }{
   // do something
}
Vlad
fonte