How to avoid nest 「null check」by “?.let”?

2019-07-23 16:30发布

问题:

let in kotlin help me avoid some if(null?) doSomething.

But I have a problem.

A is the field of the object, And B is the field of Object A. they can be nullbale.

They in code like this.

class Obj {
   var a : A?
}

class A {
   var b : B?
}

I knew I can do it by double let:

A?.let { 
   it.B.let {
      // a must nonnull
   }
}
A?.B?.let {
  // how to use A ,without null check again? 
}

回答1:

There are extension functions out there to achieve what you're looking for, you can find them in this thread https://discuss.kotlinlang.org/t/kotlin-null-check-for-multiple-nullable-vars/1946

But honestly, you're probably better of just using a basic if check here, if the variable is mutable you can assign it to a val first.

val _a = a
val _b = b
if (_a != null && _b != null) {

}

Edit: If you still really want to use let though, for this case you could create a pair and use takeIf

(a to b)
    .takeIf { (a, b) ->
        a != null && b != null
    }
    ?.let { (a, b) ->

    }

However the compiler won't smartcast the values as non-null, so you will still have to perform a non-null (!!) assertion on them.



回答2:

By default Kotlin avoids null values and for Null Safety it provides:

1) Safe Call Operator( ?. )
2) Not-Null Assertion( !! )
3) Elvis Opeartor( ?: )
4) Safe Call with let ( ?.let{...} )

  • Safe Call Operator( ?. ):Checks if the property is not null before performing any operations.

  • Not-Null Assertion( !! ) : Explicitly tells the compiler that the property is not null and if it’s null, please throw a null pointer exception (NPE)

  • Elvis Opeartor( ?: ): It's like ternary operator in java. If property is not null then left expression is returned otherwise right.

  • Safe Call with let ( ?.let{...} ): It will execute the let block only if property is not null

An example(With Not Null Value inside property):

    fun main() {
      val name: String? = "Sumit"
      println("Safe Call operator: ${name?.length}")
      name?.let {
          println("Safe Call wih let operator: ${name.length}")
      }
      val length = name?.length ?: 0
      println("Elvis operator : $length")
      println("Not Null Assertion Operator : ${name!!.length}")
   }  

Output (With Not Null Value inside property)
Safe Call operator: 5
Safe Call wih let operator: 5
Elvis operator : 5
Not Null Assertion Operator : 5

Output (With Null Value inside property)(val name: String? = null)

Safe Call operator: null
Elvis operator : 0
Exception in thread "main" kotlin.KotlinNullPointerException
  at HelloKt.main(Hello.kt:14)
  at HelloKt.main(Hello.kt)

Here, safe call with let is not executed!! And Not-Null assertion operator throws null pointer exception.

Your problem can use not-null assertion operator:

A?.B?.let {   
    // If A and B are not null then only this block will be executed.  
      A.someMethod()  
      B.someMethod()
}


回答3:

You could implement it like a Swift guard with an elvis operator ?:

fun doSomething() {
    val a = A ?: return
    val b = B ?: return
    doSomethingWith(a, b)
}

Here a and b are non-nullable references to the data you hold in In this case you'd just return from your function.



标签: kotlin null