Is there any way to chain multiple lets for multiple nullable variables in kotlin?
fun example(first: String?, second: String?) {
first?.let {
se
one more idea based on the answer by @yole
fun <T, U, R> Pair<T?, U?>.toLet(body: (List<*>) -> R): R? {
val one = first
val two = second
if (one == null || two == null)
return null
return if (one is Pair<*, *>) {
one.toLet { a ->
body(listOf(a, listOf(two)).flatten())
}
} else {
body(listOf(one, two))
}
}
so you can do the following
(1 to 6 to "a" to 4.5).toLet { (a, b, c, d) ->
// Rest of code
}
For any amount of values to be checked you can use this:
fun checkNulls(vararg elements: Any?, block: (Array<*>) -> Unit) {
elements.forEach { if (it == null) return }
block(elements.requireNoNulls())
}
And it will be used like this:
val dada: String? = null
val dede = "1"
checkNulls(dada, dede) { strings ->
}
the elements sent to the block are using the wildcard, you need to check the types if you want to access the values, if you need to use just one type you could mutate this to generics
For the case of just checking two values and also not having to work with lists:
fun <T1, T2> ifNotNull(value1: T1?, value2: T2?, bothNotNull: (T1, T2) -> (Unit)) {
if (value1 != null && value2 != null) {
bothNotNull(value1, value2)
}
}
Usage example:
var firstString: String?
var secondString: String?
ifNotNull(firstString, secondString) { first, second -> Log.d(TAG, "$first, $second") }
Actually, you can simply do this, you know? ;)
if (first != null && second != null) {
// your logic here...
}
There's nothing wrong in using a normal null-check in Kotlin.
And it's far more readable for everyone who will look into your code.
If interested here are two of my functions for solving this.
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())
}
}
Usage:
// 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)
}
I have upgraded the expected answer a bit:
inline fun <T: Any, R: Any> ifLet(vararg elements: T?, closure: (List<T>) -> R): R? {
return if (elements.all { it != null }) {
closure(elements.filterNotNull())
} else null
}
this makes this possible:
iflet("first", "sconed") {
// do somehing
} ?: run {
// do this if one of the params are null
}