In the following example, null checking is not valid as person is mutable.
class Test { fun printPerson(name: String, age: Int) { Timber.d("name=$name, age=$age") } var person = Person() fun test() { if (person.name != null && person.age != null) { printPerson(person.name, person.age) } }}
Solution 1: Local Variable
val name = person.nameval age = person.ageif (name != null && age != null) { printPerson(name, age)}
Solution 2: Nested Let
person.name?.let { name -> person.age?.let { age -> printPerson(name, age) }}
Solution 3
This solution by Dario Pellegrini has some overheads, and doesn't work well when the variables are of different types.
inline fun <T: Any> ifLet(vararg elements: T?, closure: (List<T>) -> Unit) { if (elements.all { it != null }) { closure(elements.filterNotNull()) }}
ifLet (person.name, person.age) { (name, age) -> printPerson(name as String, age as Int)}
Solution 4
This solution by Jayson Minard seems to have little overheads, but need to create multiple extensions to support different number of variables (but 5 should be enough for most cases).
// 2 variablesfun <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}// 3 variablesfun <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}// 4 variablesfun <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}// 5 variablesfun <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}
safeLet(person.name, person.age) { name, age -> printPerson(name, age)}
References: