variable - operator kotlin
Variable mĂșltiple deja entrar Kotlin (9)
Aquí hay algunas variaciones, según el estilo que desee usar, si tiene todo de tipos iguales o diferentes, y si la lista tiene un número desconocido de elementos ...
Tipos mixtos, todos no deben ser nulos para calcular un nuevo valor
Para los tipos mixtos, puede crear una serie de funciones para cada recuento de parámetros que pueden parecer tontas, pero funcionan bien para los tipos mixtos:
fun <T1: Any, T2: Any, R: Any> safeLet(p1: T1?, p2: T2?, block: (T1, T2)->R?): R? {
return if (p1 != null && p2 != null) block(p1, p2) else null
}
fun <T1: Any, T2: Any, T3: Any, R: Any> safeLet(p1: T1?, p2: T2?, p3: T3?, block: (T1, T2, T3)->R?): R? {
return if (p1 != null && p2 != null && p3 != null) block(p1, p2, p3) else null
}
fun <T1: Any, T2: Any, T3: Any, T4: Any, R: Any> safeLet(p1: T1?, p2: T2?, p3: T3?, p4: T4?, block: (T1, T2, T3, T4)->R?): R? {
return if (p1 != null && p2 != null && p3 != null && p4 != null) block(p1, p2, p3, p4) else null
}
fun <T1: Any, T2: Any, T3: Any, T4: Any, T5: Any, R: Any> safeLet(p1: T1?, p2: T2?, p3: T3?, p4: T4?, p5: T5?, block: (T1, T2, T3, T4, T5)->R?): R? {
return if (p1 != null && p2 != null && p3 != null && p4 != null && p5 != null) block(p1, p2, p3, p4, p5) else null
}
// ...keep going up to the parameter count you care about
Ejemplo de uso:
val risk = safeLet(person.name, person.age) { name, age ->
// do something
}
Ejecute bloque de código cuando la lista no tenga elementos nulos
Aquí hay dos opciones, la primera para ejecutar un bloque de código cuando una lista tiene todos los elementos no nulos, y la segunda para hacer lo mismo cuando una lista tiene al menos un elemento no nulo. Ambos casos pasan una lista de elementos no nulos al bloque de código:
Funciones:
fun <T: Any, R: Any> Collection<T?>.whenAllNotNull(block: (List<T>)->R) {
if (this.all { it != null }) {
block(this.filterNotNull()) // or do unsafe cast to non null collectino
}
}
fun <T: Any, R: Any> Collection<T?>.whenAnyNotNull(block: (List<T>)->R) {
if (this.any { it != null }) {
block(this.filterNotNull())
}
}
Ejemplo de uso:
listOf("something", "else", "matters").whenAllNotNull {
println(it.joinToString(" "))
} // output "something else matters"
listOf("something", null, "matters").whenAllNotNull {
println(it.joinToString(" "))
} // no output
listOf("something", null, "matters").whenAnyNotNull {
println(it.joinToString(" "))
} // output "something matters"
Un ligero cambio para que la función reciba la lista de elementos y realice las mismas operaciones:
fun <T: Any, R: Any> whenAllNotNull(vararg options: T?, block: (List<T>)->R) {
if (options.all { it != null }) {
block(options.filterNotNull()) // or do unsafe cast to non null collection
}
}
fun <T: Any, R: Any> whenAnyNotNull(vararg options: T?, block: (List<T>)->R) {
if (options.any { it != null }) {
block(options.filterNotNull())
}
}
Ejemplo de uso:
whenAllNotNull("something", "else", "matters") {
println(it.joinToString(" "))
} // output "something else matters"
Estas variaciones podrían cambiarse para tener valores de retorno como
let()
.
Utilice el primer elemento no nulo (fusión)
Similar a una función de fusión SQL, devuelve el primer elemento no nulo. Dos sabores de la función:
fun <T: Any> coalesce(vararg options: T?): T? = options.firstOrNull { it != null }
fun <T: Any> Collection<T?>.coalesce(): T? = this.firstOrNull { it != null }
Ejemplo de uso:
coalesce(null, "something", null, "matters")?.let {
it.length
} // result is 9, length of "something"
listOf(null, "something", null, "matters").coalesce()?.let {
it.length
} // result is 9, length of "something"
Otras variaciones
... Hay otras variaciones, pero con una mayor especificación, esto podría reducirse.
¿Hay alguna forma de encadenar múltiples permisos para múltiples variables anulables en kotlin?
fun example(first: String?, second: String?) {
first?.let {
second?.let {
// Do something just if both are != null
}
}
}
Quiero decir, algo como esto:
fun example(first: String?, second: String?) {
first?.let && second?.let {
// Do something just if both are != null
}
}
De hecho, prefiero resolverlo usando las siguientes funciones auxiliares:
fun <A, B> T(tuple: Pair<A?, B?>): Pair<A, B>? =
if(tuple.first == null || tuple.second == null) null
else Pair(tuple.first!!, tuple.second!!)
fun <A, B, C> T(tuple: Triple<A?, B?, C?>): Triple<A, B, C>? =
if(tuple.first == null || tuple.second == null || tuple.third == null) null
else Triple(tuple.first!!, tuple.second!!, tuple.third!!)
fun <A, B> T(first: A?, second: B?): Pair<A, B>? =
if(first == null || second == null) null
else Pair(first, second)
fun <A, B, C> T(first: A?, second: B?, third: C?): Triple<A, B, C>? =
if(first == null || second == null || third == null) null
else Triple(first, second, third)
Y así es como debe usarlos:
val a: A? = someValue
val b: B? = someOtherValue
T(a, b)?.let { (a, b) ->
// Shadowed a and b are of type a: A and b: B
val c: C? = anotherValue
T(a, b, c)
}?.let { (a, b, c) ->
// Shadowed a, b and c are of type a: A, b: B and c: C
.
.
.
}
En el momento de escribir este artículo, Kotlin 1.3 no tiene un mecanismo nativo para admitir múltiples y encadenados en un lindo revestimiento.
El lenguaje Swift admite múltiples y encadenados permite muy elegantemente (en mi opinión) a través de
if let a = a, let b = b, let c = a.doSomething(b) {
print(c)
} else {
print("something was null")
}
Me entristece cada vez que necesito hacer algo como lo anterior en Kotlin porque el código termina siendo detallado y un poco feo.
¡Con suerte obtendremos algo similar en Kotlin uno de estos días!
En realidad, simplemente puedes hacer esto, ¿sabes? ;)
if (first != null && second != null) {
// your logic here...
}
No hay nada de malo en usar un cheque nulo normal en Kotlin.
Y es mucho más legible para todos los que analizarán su código.
Para cualquier cantidad de valores a comprobar, puede usar esto:
fun checkNulls(vararg elements: Any?, block: (Array<*>) -> Unit) {
elements.forEach { if (it == null) return }
block(elements.requireNoNulls())
}
Y se usará así:
val dada: String? = null
val dede = "1"
checkNulls(dada, dede) { strings ->
}
los elementos enviados al bloque están usando el comodín, debe verificar los tipos si desea acceder a los valores, si necesita usar solo un tipo, puede mutar esto a genéricos
Para el caso de simplemente verificar dos valores y no tener que trabajar con listas:
fun <T1, T2> ifNotNull(value1: T1?, value2: T2?, bothNotNull: (T1, T2) -> (Unit)) {
if (value1 != null && value2 != null) {
bothNotNull(value1, value2)
}
}
Ejemplo de uso:
var firstString: String?
var secondString: String?
ifNotNull(firstString, secondString) { first, second -> Log.d(TAG, "$first, $second") }
Puede crear una función
arrayIfNoNulls
:
fun <T : Any> arrayIfNoNulls(vararg elements: T?): Array<T>? {
if (null in elements) {
return null
}
@Suppress("UNCHECKED_CAST")
return elements as Array<T>
}
Luego puede usarlo para un número variable de valores con
let
:
fun example(first: String?, second: String?) {
arrayIfNoNulls(first, second)?.let { (first, second) ->
// Do something if each element is not null
}
}
Si ya tiene una matriz, puede crear una función
takeIfNoNulls
(inspirada en
takeIf
y
requireNoNulls
):
fun <T : Any> Array<T?>.takeIfNoNulls(): Array<T>? {
if (null in this) {
return null
}
@Suppress("UNCHECKED_CAST")
return this as Array<T>
}
Ejemplo:
array?.takeIfNoNulls()?.let { (first, second) ->
// Do something if each element is not null
}
Puede escribir su propia función para eso:
fun <T, U, R> Pair<T?, U?>.biLet(body: (T, U) -> R): R? {
val first = first
val second = second
if (first != null && second != null) {
return body(first, second)
}
return null
}
(first to second).biLet { first, second ->
// body
}
Si está interesado aquí hay dos de mis funciones para resolver esto.
inline fun <T: Any> guardLet(vararg elements: T?, closure: () -> Nothing): List<T> {
return if (elements.all { it != null }) {
elements.filterNotNull()
} else {
closure()
}
}
inline fun <T: Any> ifLet(vararg elements: T?, closure: (List<T>) -> Unit) {
if (elements.all { it != null }) {
closure(elements.filterNotNull())
}
}
Uso:
// Will print
val (first, second, third) = guardLet("Hello", 3, Thing("Hello")) { return }
println(first)
println(second)
println(third)
// Will return
val (first, second, third) = guardLet("Hello", null, Thing("Hello")) { return }
println(first)
println(second)
println(third)
// Will print
ifLet("Hello", "A", 9) {
(first, second, third) ->
println(first)
println(second)
println(third)
}
// Won''t print
ifLet("Hello", 9, null) {
(first, second, third) ->
println(first)
println(second)
println(third)
}