Supposons que nous ayons ce code:
class QuickExample {
fun function(argument: SomeOtherClass) {
if (argument.mutableProperty != null ) {
doSomething(argument.mutableProperty)
} else {
doOtherThing()
}
}
fun doSomething(argument: Object) {}
fun doOtherThing() {}
}
class SomeOtherClass {
var mutableProperty: Object? = null
}
Contrairement à Java, où vous pourriez vous retrouver seul à vous soucier de la déréférencement nul lors de l'exécution, cela ne compile pas - à juste titre. Bien entendu, mutableProperty
peut ne plus être nul une fois dans le 'if'.
Ma question est quelle est la meilleure façon de gérer cela?
Quelques options sont apparentes. Sans utiliser de nouvelles fonctionnalités du langage Kotlin, le moyen le plus simple est évidemment de copier la valeur dans une méthode qui ne changera pas par la suite.
Il y a ceci:
fun function(argument: SomeOtherClass) {
argument.mutableProperty?.let {
doSomething(it)
return
}
doOtherThing()
}
Cela présente l’inconvénient évident de devoir revenir plus tôt ou d’éviter d’exécuter le code suivant - OK dans certains petits contextes, mais cela a une odeur.
Ensuite, il y a cette possibilité:
fun function(argument: SomeOtherClass) {
argument.mutableProperty.let {
when {
it != null -> {
doSomething(it)
}
else -> {
doOtherThing()
}
}
}
}
mais bien que son objectif soit plus clair, il est plus difficile à manier et à commenter que la façon de le faire en Java.
Est-ce que je manque quelque chose et y a-t-il un idiome préféré pour y parvenir?
Je ne crois pas qu'il existe un moyen vraiment "court" d'y parvenir, mais vous pouvez simplement utiliser un conditionnel dans with
ou let
:
with(mutableVar) { if (this != null) doSomething(this) else doOtherThing() }
mutableVar.let { if (it != null) doSomething(it) else doOtherThing() }
En fait, "capturer" une valeur mutable est l’un des principaux cas d’utilisation de let
.
Cela équivaut à votre déclaration when
.
Il y a toujours l'option que vous avez décrite, en l'affectant à une variable:
val immutable = mutableVar
if (immutable != null) {
doSomething(immutable)
} else {
doOtherThing()
}
qui est toujours une solution de rechange de Nice au cas par exemple. les choses deviennent trop verbeuses.
Il n’ya probablement pas vraiment de très Nice moyen d’y parvenir car seul l’argument last lambda est autorisé à être placé en dehors du ()
, donc spécifier deux ne correspondrait pas vraiment à la syntaxe de autres fonctions standard.
Vous pourriez en écrire un si ça ne vous dérange pas (ou si vous allez passer des références de méthodes):
inline fun <T : Any, R> T?.ifNotNullOrElse(ifNotNullPath: (T) -> R, elsePath: () -> R)
= let { if(it == null) elsePath() else ifNotNullPath(it) }
...
val a: Int? = null
a.ifNotNullOrElse({ println("not null") }, { println("null") })
Mettre à jour:
Comme mentionné par franta dans les commentaires, si la méthode doSomething()
renvoie la valeur null, le code situé du côté utilisation de l'opérateur elvis sera exécuté, ce qui pourrait ne pas être le cas souhaité. Mais dans le même temps, dans ce cas, il est très probable que la méthode doSomething()
ne fera que faire quelque chose et ne retourne rien.
Et une alternative: comme Protossor l'a mentionné dans les commentaires, also
peut être utilisé plutôt que let
, car also
renvoie l'objet this
au lieu du résultat du bloc de fonction.
mutableProperty?.also { doSomething(it) } ?: doOtherThing()
Réponse originale:
Je voudrais utiliser let
avec opérateur Elvis .
mutableProperty?.let { doSomething(it) } ?: doOtherThing()
De la doc:
Si l'expression à gauche de?: N'est pas nulle, l'opérateur elvis le retourne, sinon il retourne l'expression à droite. Remarque que l'expression de droite n'est évaluée que si la main gauche côté est nul.
Pour un bloc de code après l'expression de droite:
mutableProperty?.let {
doSomething(it)
} ?: run {
doOtherThing()
doOtherThing()
}
ajouter une fonction en ligne personnalisée comme ci-dessous:
inline fun <T> T?.whenNull(block: T?.() -> Unit): T? {
if (this == null) block()
return this@whenNull
}
inline fun <T> T?.whenNonNull(block: T.() -> Unit): T? {
this?.block()
return this@whenNonNull
}
alors vous pouvez écrire le code comme ceci:
var nullableVariable :Any? = null
nullableVariable.whenNonNull {
doSomething(nullableVariable)
}.whenNull {
doOtherThing()
}
je l'écris habituellement comme ceci:
takeIf{somecondition}?.also{put somecondition is met code}?:run{put your else code here}
notez le point d'interrogation après takeIf est un MUST. vous pouvez également utiliser ou appliquer un mot clé.