The best way to handle a scenario where "smart casting is impossible"

Interestingly, the best way to handle this scenario is

class Person(var name:String? = null, var age:Int? = null){ fun test(){ if(name != null && age != null) doSth(name, age) //smart cast imposible } fun doSth (someValue:String, someValue2:Int){ } } 

What is the easiest way to call the doSth method and make sure the name and age are nt null?

I'm looking for something simple, as with a single variable script, where I just used let

 name?.let{ doSth(it) } 
+6
source share
6 answers

You can embed let as much as you want:

 fun test(){ name?.let { name -> age?.let { age -> doSth(name, age) //smart cast imposible } } } 

Another approach that may be easier to follow is to use local variables:

 fun test(){ val name = name val age = age if(name != null && age != null){ doSth(name, age) } } 

Last but not least, consider changing Person as immutable:

 data class Person(val name:String? = null, val age:Int? = null){ fun test(){ if(name != null && age != null){ doSth(name, age) } } ... } 
+8
source

To make a throw, you must somehow make a local copy of the value. In Kotlin, this is best done explicitly:

 val name = name val age = age if(name != null && age != null){ doSth(name, age) } 

The let function hides this behind a layer of abstraction, which is not the best IMHO.

+3
source

There is a nice small library that allows you to write let code with several variables. It is open source and you can find it on GitHub, it is called Unwrap

An example based on readme:

 unwrap(_a, _b, _c) { a, b, c -> println("$a, $b$c") // all variables are not-null } 

All unwrap(...) methods are marked inline , so there should be no overhead with their use.

By the way, this lib also allows you to handle the situation when there are several null variables ( nah() method).

+2
source

If you want to make this a bit β€œextreme,” you can define an extension function on Pair<String?,Int?> That hides the logic for you:

 fun Pair<String?,Int?>.test(block: (String, Int) -> Unit) { if(first != null && second != null) { block(first, second) } } 

then his call will be a little shorter

 (name to age).test { n, a -> println("name: $n age: $a") } 

However, this will not help you (since you could also define this as a function within the Person class itself) if you do not need such functionality very often throughout the project. As I said, this seems redundant.

change you could make it (a little) more useful by going all the way to the generic:

 fun <T,R> Pair<T?,R?>.ifBothNotNull(block: (T, R) -> Unit) { if(first != null && second != null){ block(first, second) } } 
0
source

In addition to miensol answer, there are various ways to copy property values ​​into function variables to enable smart casting. eg:.

  • Intermediary function:

     class Person(var name: String? = null, var age: Int? = null) { fun test() = test(name, age) private fun test(name: String?, age: Int?) { if (name != null && age != null) doSth(name, age) //smart cast possible } fun doSth(someValue: String, someValue2: Int) { } } 
  • Anonymous function:

     class Person(var name: String? = null, var age: Int? = null) { fun test() = (fun(name: String?, age: Int?) { if (name != null && age != null) doSth(name, age) //smart cast possible })(name, age) fun doSth(someValue: String, someValue2: Int) { } } 
  • The default arguments are:

     class Person(var name: String? = null, var age: Int? = null) { fun test(name: String? = this.name, age: Int? = this.age) { if (name != null && age != null) doSth(name, age) //smart cast possible } fun doSth(someValue: String, someValue2: Int) { } } 
0
source

You can define a built-in method that allows you to take N parameters to avoid let nesting (I base my answer on this ).

 inline 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 } 

Then

 fun test() { safeLet(name, age, {name, age -> doSth(name, age) //smart cast }); } 
0
source

All Articles