android - practices - kotlin long
Kotlin y forma idiomática de escribir, "si no es nulo, sino..." basada en un valor mutable (5)
Agregue la función en línea personalizada como se muestra a continuación:
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
}
Entonces puedes escribir código como este:
var nullableVariable :Any? = null
nullableVariable.whenNonNull {
doSomething(nullableVariable)
}.whenNull {
doOtherThing()
}
Supongamos que tenemos este código:
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
}
A diferencia de Java, donde puede quedarse solo para preocuparse por la anulación de referencias nulas en el tiempo de ejecución, esto no compila, con toda razón. Por supuesto, mutableProperty
ya no puede ser nulo una vez dentro del ''if''.
Mi pregunta es ¿cuál es la mejor manera de manejar esto?
Algunas opciones son evidentes. Sin utilizar ninguna de las nuevas funciones de lenguaje Kotlin, la forma más sencilla es, obviamente, copiar el valor en un método de alcance que no cambie posteriormente.
Hay esto:
fun function(argument: SomeOtherClass) {
argument.mutableProperty?.let {
doSomething(it)
return
}
doOtherThing()
}
Esto tiene la desventaja obvia de que debe regresar antes o, de lo contrario, evitar ejecutar el código subsiguiente. Está bien en ciertos contextos pequeños, pero tiene un olor.
Luego está esta posibilidad:
fun function(argument: SomeOtherClass) {
argument.mutableProperty.let {
when {
it != null -> {
doSomething(it)
}
else -> {
doOtherThing()
}
}
}
}
pero si bien tiene una mayor claridad de propósito, podría decirse que es más difícil de manejar y más detallado que la forma al estilo de Java de lidiar con esto.
¿Me estoy perdiendo algo, y hay un idioma preferido con el que lograrlo?
No creo que haya una manera realmente "corta" de lograrlo, sin embargo, simplemente puede usar un condicional dentro o let
:
with(mutableVar) { if (this != null) doSomething(this) else doOtherThing() }
mutableVar.let { if (it != null) doSomething(it) else doOtherThing() }
De hecho, "capturar" un valor mutable es uno de los principales casos de uso de let
.
Esto es equivalente a su declaración when
.
Siempre existe la opción que describiste, asignándola a una variable:
val immutable = mutableVar
if (immutable != null) {
doSomething(immutable)
} else {
doOtherThing()
}
que siempre es una buena alternativa en caso de que, por ejemplo, las cosas se vuelvan demasiado detalladas.
Probablemente no haya una manera muy agradable de lograr esto porque solo se permite colocar el último argumento lambda fuera de ()
, por lo que especificar dos no se ajustaría realmente a la sintaxis de todas las demás funciones estándar.
Podría escribir uno si no le importa (o si en su lugar va a pasar referencias a métodos):
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") })
Normalmente lo escribo así:
takeIf{somecondition}?.also{put somecondition is met code}?:run{put your else code here}
tenga en cuenta el signo de interrogación después de takeIf es un deber. Puede utilizar también o aplicar palabras clave.
También podrías hacer algo como esto:
class If<T>(val any: T?, private val i: (T) -> Unit) {
infix fun Else(e: () -> Unit) {
if (any == null) e()
else i(any)
}
}
Puedes usarlo así:
If(nullableString) {
//Use string
} Else {
}
Actualizar:
Como lo mencionó franta en los comentarios, si el método doSomething()
devuelve un valor nulo, entonces se ejecutará el código en el lado derecho del operador elvis, que podría no ser el caso deseado para la mayoría. Pero al mismo tiempo, en este caso, es muy probable que el método doSomething()
solo haga algo y no devuelva nada.
Y una alternativa: como protossor ha mencionado en los comentarios, also
se puede usar en lugar de let
, porque also
devuelve this
objeto en lugar del resultado del bloque de funciones.
mutableProperty?.also { doSomething(it) } ?: doOtherThing()
Respuesta original:
Yo usaría let
con el operador Elvis .
mutableProperty?.let { doSomething(it) } ?: doOtherThing()
Desde el doc:
Si la expresión a la izquierda de?: No es nula, el operador de elvis la devuelve, de lo contrario, devuelve la expresión a la derecha. Tenga en cuenta que la expresión del lado derecho solo se evalúa si el lado izquierdo es nulo.
Para un bloque de código después de la expresión del lado derecho:
mutableProperty?.let {
doSomething(it)
} ?: run {
doOtherThing()
doOtherThing()
}