Can I smartCast within the when statement? - kotlin

I have the code below
class Sample {
var variable1: SomeClass? = null
var variable2: SomeClass? = null
var variable3: SomeClass? = null
fun checkVariable() {
when {
variable1 != null -> variable1!!.doSomething()
variable2 != null -> variable2!!.doSomething()
variable3 != null -> variable3!!.doSomething()
}
}
}
I'm hoping I can make variableX after the -> non-nullable, so I don't need to !!.
I can avoid !! with
variable1 !== null -> variable1?.doSomething()
But is there away to do this more elegantly that I can have a non-nullable variable to access the doSomething()?

The error is not because of the when. You just cannot smart cast class-level vars.
In this case, since all your variables are of the same type and you are doing the same thing on all of them, your code can be simplified to:
(variable1 ?: variable2 ?: variable3)?.doSomething()
In other words, you are finding the first non-null out of the three variables, and calling doSomething on it.
If your variables are not of the same type, and you are doing different things to each of them, you can do:
variable1?.also {
it.doSomething()
} ?: variable2?.also {
it.doSomethingElse()
} ?: variable3?.also {
it.doAnotherThing()
}

Not sure if it's more elegant but you could maybe write this instead:
fun getStrLength() = (variable1 ?: variable2 ?: variable3)?.doSomething()
Another way to remove the !! is to first store them in local variables like
fun getStrLengths() {
val variable1 = variable1
val variable2 = variable2
val variable3 = variable3
when {
variable1 != null -> variable1.doSomething()
variable2 != null -> variable2.doSomething()
variable3 != null -> variable3.doSomething()
}
}

Lazy initialisation
You could make the properties non-nullable with lateinit var.
This would prevent the need for any null checks.
You can check to see if the properties are present with isInitialized instead of a non-null check.
class Sample {
lateinit var variable1: SomeClass
lateinit var variable2: SomeClass
lateinit var variable3: SomeClass
fun checkVariable() {
when {
// so long as a value is initialised, there is no need for null checks
::variable1.isInitialized -> variable1.printName()
::variable2.isInitialized -> variable2.printName()
::variable3.isInitialized -> variable3.printName()
}
}
}
class SomeClass(val name: String) {
fun printName() {
println(name)
}
}
Unsetting values
This can be useful to avoid null checks, but it would prevent 'unsetting' previously set variables with null.
val sample = Sample()
sample.variable1 = SomeClass("foo")
sample.variable1 = null // ERROR: Null can not be a value of a non-null type SomeClass
Whether unsetting values is required or not depends on your use-case.
Example
fun main() {
val sample = Sample()
println("first check:")
sample.checkVariable()
println("---")
sample.variable3 = SomeClass("Jamie")
println("second check:")
sample.checkVariable()
println("---")
sample.variable2 = SomeClass("Maddie")
println("third check:")
sample.checkVariable()
println("---")
sample.variable1 = SomeClass("Lisa")
println("fourth check:")
sample.checkVariable()
println("---")
}
We can see that as each variable is set, other variables are not called, as they are listed lower in the when statement.
first check:
---
second check:
Jamie
---
third check:
Maddie
---
fourth check:
Lisa
---

Related

Set a value to builder if not null - Kotlin

I am new to Kotlin. Can someone tell me how can I set the value only if not null here in below cade. There should be a way to use with let() but I am not sure how to do it.
If the var2 is not null only I should set it. Otherwise null pointer error will throw.
private fun myFunc(var1: Type1 , var2: Type2?) {
val request = class1.newBuilder()
.setType1(var1)
.setType2(var2) // how to set var2 only if not null?
.build()
clientClass.send(request)
}
If each builder function returns a new Builder instance, use run:
private fun myFunc(var1: Type1 , var2: Type2?) {
val request = class1.newBuilder()
.setType1(var1)
.run { if(var2 != null) setType2(var2) else this }
.build()
clientClass.send(request)
}
If the builder functions mutate and return the same Builder instance, it’s simpler to use apply
private fun myFunc(var1: Type1 , var2: Type2?) {
val request = class1.newBuilder()
.setType1(var1)
.apply { if(var2 != null) setType2(var2) }
.build()
clientClass.send(request)
}
// or more cleanly using apply for everything instead of chaining:
private fun myFunc(var1: Type1 , var2: Type2?) {
val request = class1.newBuilder().apply {
setType1(var1)
if(var2 != null) setType2(var2)
build()
}
clientClass.send(request)
}
Example of a Builder class whose functions return new instances:
fun setType2(type2: Type2): Builder {
return CombinedBuilder(this, type2) // a new object
}
Example of a Builder class whose functions return the same instance:
fun setType2(type2: Type2): Builder {
this.type2 = type2
return this // the same object
}
The second type is more common, but sometimes the first type is used. You might have to check the source code to know for sure. If you can't be sure, use the .run method because it will work for either.
Using scope functions is one way to solve this but results in code that is hard to read imo. If you stick to the builder pattern, the simplest solution would be to modify the builder to accept Null values:
class Builder {
private var type1: Type1 = <default>
private var type2: Type2 = <defaul>
fun setType1(type: Type1?): Builder {
if (type != null) this.type1 = type
return this
}
fun setType2(type: Type2?): Builder {
if (type != null) this.type2 = type
return this
}
}
That way you can keep:
class1.newBuilder()
.setType1(var1)
.setType2(var2)
.build()
To communicate the fact that the values won't be set if they are Null, change the name of the setters to e.g. setTypeIfNotNull.
With Kotlin the builder pattern is kind of obsolete (unless you create a DSL) because you can use a constructor with default and named arguments instead, something like:
class Request(val type1: Type1 = <default>, val type2: Type2 = <default>)
Then you can do:
Request(
type1 = type1,
type2 = type2,
)
Now this doesn't cover the Null case but you can use this to accept null values:
companion object {
public operator fun invoke(type1: Type1, type2: Type2? = null) = Request(type1, type2 ?: <default value>)
}
The reason why there's a companion object instead of a constructor is explained here: Is there a way to use the default value on a non-optional parameter when null is passed?.

Kotlin non-null assertion with safe operator

Why this code is giving me the NPE error?
fun main() {
val myObj: MyObj? = null
print(myObj?.someVal!!)
}
class MyObj{
val someVal = 1
}
Does non-null assertion evaluate the whole expression before? I thought that myObj? should be enough to print null here.
Yes. When you use a null-safe call, the expression is evaluated to null if the receiver is null, so
myObj?.someVal!!
behaves the same as
(if (myObj != null) myObj.someVal else null)!!
(If myObj is a property rather than local variable, these aren't equivalent, since smart-casting wouldn't work.)
Simple example:
This code will print null:
fun main() {
val myObj: MyObj? = null
val someVal = myObj?.someVal
print(someVal)
}
This code will throw NPE as the variable is null:
fun main() {
val myObj: MyObj? = null
val someVal = myObj?.someVal
print(someVal!!)
}

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

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?
}
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.
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.
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
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()
}
You can use when as an or statement for multiple variables
when(null) {
a, b -> return //for your case of nested properties try something like "obj.a, obj?.a?.b"
else -> doSomethingWhenAAndBAreNotNull()
}
//or do something when a and b are not null here so you don't need to nest
Another option could be to write something like this
fun <A, B> A?.and(that: B?, block: (A, B) -> Unit) {
this?.let { a -> that?.let { b -> block(a, b) } }
}
and then use it like this
var first: Int?
var second: Int?
first.and(second) { f, s -> someFunction(f,s) }

Swift 'if let' statement equivalent in Kotlin

In Kotlin is there an equivalent to the Swift code below?
if let a = b.val {
} else {
}
You can use the let-function like this:
val a = b?.let {
// If b is not null.
} ?: run {
// If b is null.
}
Note that you need to call the run function only if you need a block of code. You can remove the run-block if you only have a oneliner after the elvis-operator (?:).
Be aware that the run block will be evaluated either if b is null, or if the let-block evaluates to null.
Because of this, you usually want just an if expression.
val a = if (b == null) {
// ...
} else {
// ...
}
In this case, the else-block will only be evaluated if b is not null.
Let's first ensure we understand the semantics of the provided Swift idiom:
if let a = <expr> {
// then-block
}
else {
// else-block
}
It means this: "if the <expr> results in a non-nil optional, enter the then-block with the symbol a bound to the unwrapped value. Otherwise enter the else block.
Especially note that a is bound only within the then-block. In Kotlin you can easily get this by calling
<expr>?.also { a ->
// then-block
}
and you can add an else-block like this:
<expr>?.also { a ->
// then-block
} ?: run {
// else-block
}
This results in the same semantics as the Swift idiom.
My answer is totally a copy cat from the others. However, I cannot understand their expression easily. So I guess it would be nice to provide an more understandable answer.
In swift:
if let a = b.val {
//use "a" as unwrapped
}
else {
}
In Kotlin:
b.val?.let{a ->
//use "a" as unwrapped
} ?: run{
//else case
}
if let statement
Swift if let Optional Binding (so called if-let statement) is used to extract a non-optional value if one exists, or to do nothing if a value is nil.
Swift's if-let statement:
let b: Int? = 50
if let a: Int = b {
print("Good news!")
} else {
print("Equal to 'nil' or not set")
}
/* RESULT: Good news! */
In Kotlin, like in Swift, to avoid crashes caused by trying to access a null value when it’s not expected, a specific syntax (like b.let { } in second example) is provided for properly unwrapping nullable types:
Kotlin's equivalent1 of Swift's if-let statement:
val b: Int? = null
val a = b
if (a != null) {
println("Good news!")
} else {
println("Equal to 'null' or not set")
}
/* RESULT: Equal to 'null' or not set */
Kotlin’s let method, when used in combination with the safe-call operator ?:, provides a concise way to handle nullable expressions.
Kotlin's inline let function and Elvis Operator of Swift's nil coalescing operator:
val b: Int? = null
val a = b.let { nonNullable -> nonNullable } ?: "Equal to 'null' or not set"
println(a)
/* RESULT: Equal to 'null' or not set */
guard let statement
guard-let statement in Swift is simple and powerful. It checks for some condition and if it evaluates to be false, then the else statement executes which normally will exit a method.
Let's explore a Swift's guard-let statement:
let b: Int? = nil
func method() {
guard let a: Int = b else {
print("Equal to 'nil' or not set")
return
}
print("Good news!")
}
method()
/* RESULT: Equal to 'nil' or not set */
Kotlin's similar effect of Swift's guard-let statement:
Unlike Swift, in Kotlin, there is no guard statement at all. However, you can use the Elvis Operator – ?: for getting a similar effect.
val b: Int? = 50
fun method() {
val a = b ?: return println("Equal to 'null' or not set")
return println("Good news!")
}
method()
/* RESULT: Good news! */
there are two answers above, both got a lot acceptances:
str?.let{ } ?: run { }
str?.also{ } ?: run { }
Both seem to work in most of the usages, but #1 would fail in the following test:
#2 seems better.
Unlike Swift, Its not necessary to unwrap the optional before using it in Kotlin. We could just check if the value is non null and the compiler tracks the information about the check you performed and allows to use it as unwrapped.
In Swift:
if let a = b.val {
//use "a" as unwrapped
} else {
}
In Kotlin:
if b.val != null {
//use "b.val" as unwrapped
} else {
}
Refer Documentation: (null-safety) for more such use cases
Here's how to only execute code when name is not null:
var name: String? = null
name?.let { nameUnwrapp ->
println(nameUnwrapp) // not printed because name was null
}
name = "Alex"
name?.let { nameUnwrapp ->
println(nameUnwrapp) // printed "Alex"
}
Here's my variant, limited to the very common "if not null" case.
First of all, define this somewhere:
inline fun <T> ifNotNull(obj: T?, block: (T) -> Unit) {
if (obj != null) {
block(obj)
}
}
It should probably be internal, to avoid conflicts.
Now, convert this Swift code:
if let item = obj.item {
doSomething(item)
}
To this Kotlin code:
ifNotNull(obj.item) { item ->
doSomething(item)
}
Note that as always with blocks in Kotlin, you can drop the argument and use it:
ifNotNull(obj.item) {
doSomething(it)
}
But if the block is more than 1-2 lines, it's probably best to be explicit.
This is as similar to Swift as I could find.
There is a similar way in kotlin to achieve Swift's style if-let
if (val a = b) {
a.doFirst()
a.doSecond()
}
You can also assigned multiple nullable values
if (val name = nullableName, val age = nullableAge) {
doSomething(name, age)
}
This kind of approach will be more suitable if the nullable values is used for more than 1 times. In my opinion, it helps from the performance aspect because the nullable value will be checked only once.
source: Kotlin Discussion
I'm adding this answer to clarify the accepted answer because it's too big for a comment.
The general pattern here is that you can use any combination of the Scope Functions available in Kotlin separated by the Elvis Operator like this:
<nullable>?.<scope function> {
// code if not null
} :? <scope function> {
// code if null
}
For example:
val gradedStudent = student?.apply {
grade = newGrade
} :? with(newGrade) {
Student().apply { grade = newGrade }
}
The cleanest option in my opinion is this
Swift:
if let a = b.val {
} else {
}
Kotlin
b.val.also { a ->
} ?: run {
}
Swift if let statement in Kotlin
The short answer is use simple IF-ELSE as by the time of this comment there is no equivalent in Kotlin LET,
if(A.isNull()){
// A is null
}else{
// A is not null
}
we can get the same Unwraping syntax like Swift if let using inline fun
inline fun <T:Any?> T?.unwrap(callback: (T)-> Unit) : Boolean {
return if (this != null) {
this?.let(callback)
true
}else {
false
}
}
Uses: :
val name : String? = null
val rollNo : String? = ""
var namesList: ArrayList<String>? = null
if (name.unwrap { name ->
Log.i("Dhiru", "Name have value on it $name")
})else if ( rollNo.unwrap {
Log.i("Dhiru","Roll have value on it")
}) else if (namesList.unwrap { namesList ->
Log.i("Dhiru","This is Called when names list have value ")
}) {
Log.i("Dhiru","No Field have value on it ")
}
The problem with the Any?.let {} ?: run {} constructions is that:
It only allows for one non-null check per statement
If the let block returns null the run block is evaluated anyway
It's not possible to perform multiple checks in a switch/when style
A solution which tackles most of these problems is to define functions like the following:
private inline fun <A> ifNotNull(p1: A?, block: (A) -> Unit): Unit? {
if (p1 != null) {
return block.invoke(p1)
}
return null
}
private inline fun <A, B> ifNotNull(p1: A?, p2: B?, block: (A, B) -> Unit): Unit? {
if (p1 != null && p2 != null) {
return block.invoke(p1, p2)
}
return null
}
private inline fun <A, B, C> ifNotNull(p1: A?, p2: B?, p3: C?, block: (A, B, C) -> Unit): Unit? {
if (p1 != null && p2 != null && p3 != null) {
return block.invoke(p1, p2, p3)
}
return null
}
This would allow for a statement like:
ifNotNull(a, b) { a, b ->
// code when a, b are not null
} ?:
ifNotNull(c) { c ->
// code when a, b are null and c not null
} ?:
ifNotNull(d, e, f) { d, e, f ->
// code when a, b, c are null and d, e, f not null
} ?: run {
// code which should be performed if a, b, c, d, e and f are null
}
The only caveat is that continue and break statements are not supported if executed within a loop compared to Swift's if let equivalent.
Probably I am very late however the easiest way to unwrap and option is
yourOptionalString ?: return
this was all the following lines will have unwrapped string
If b is a member variable then this approach seems most readable to me:
val b = this.b
if (b == null) {
return
}
println("non nullable : ${b}")
This is also consistent with how it works in swift, where a new local variable shadows the member variable.

Having a getter return a non-nullable type even though the backing field is nullable

num should be nullable when set, but what it returns should always be non-nullable (have a default value).
class Test {
var num: Int? = null
get() = field ?: 5 // default value if null
}
The following does not compile even though the returned value is always non-null which makes sense to me, because the type is not inferred but taken from the backing field:
val a: Int = Test().num
Type mismatch: inferred type is Int? but Int was expected
The question is how can I change the return type of that getter to be non-nullable? If I do so, the compiler says:
Getter return type must be equal to the type of the property, i.e.
'Int?'
I know that I could solve it with another property numNotNullable (without a backing field).
class Test {
var num: Int? = null
get() = field ?: 5 // default value if null
val numNotNullable: Int
get() = num ?: 5
}
val c: Int = Test().numNotNullable
But this is not what I want. Is there another way?
var num: Int? = null
This is your property signature. It doesn't matter, if you internally ensure that no null value is returned. The signature says, that the value is nullable.
This implicates:
You are allowed to set null to this field
All classes using this field, must handle the fact that the property can return null
Your Solution with a second property is good.
You of course can replace the property with plain old java bean, but I wouldn't advise that, because than you have to access the prop with getNumb and setNum.
class Test {
private var num: Int = 5
fun setNum(num: Int?) {
this.num = num ?: 5
}
fun getNum() = num
}
I don't believe this is possible in Kotlin. You can't override the type of the the property for get/set. So if your property is an Int? you're going to have to return an Int? and check if it is null when you use it.
There's technically a feature request for what you're looking for, but it's been years since it was made.
You can achive this using delegated properties
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class LazyVar<T : Any>(private var initializer: () -> T) : ReadWriteProperty<Any?, T> {
private var value: T? = null
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
if (value == null) {
value = initializer()
print(value)
}
return value as T
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = value
}
}
class Test {
var num: Int by LazyVar { 5 }
}
val a: Int = Test().num
Note, that this code is not thread-safe. Also with this code sample you can't set null value for you field (so no way back to default value).