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!!)
}
Related
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
---
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) }
Fairly new to Kotlin - I'm slightly confused by why
when using let in this example 'it' is still nullable?
As a result the example fails to compile
Error:(9, 20) Type mismatch: inferred type is Int? but Int was
expected
fun main(args: Array<String>) {
val myNewNullableInt: Int? =6
myNewNullableInt.let{
printAnInteger(it)
}
}
fun printAnInteger (integerToPrint: Int){
println(integerToPrint)
}
Example:
https://try.kotlinlang.org/#/UserProjects/fnlicstrn4tbmk8gs071vmv4ka/9dd72cr92ikggh3g9qg981h01o
let {} doesn't change the type of the variable. What you want to do is making it null-safe. This is done with the ?. access
myNewNullableInt?.let{
printAnInteger(it)
}
See https://kotlinlang.org/docs/reference/null-safety.html
You have to use the safe call operator, written ?.
To perform a certain operation only for non-null values, you can use
the safe call operator together with let:
val listWithNulls: List<String?> = listOf("Kotlin", null)
for (item in listWithNulls) {
item?.let { println(it) } // prints A and ignores null
}
Other options:
As second option you can use the !!-operator (use with care, it will throw a NPE if it is null)
val myNewNullableInt: Int? =6
myNewNullableInt!!.let{ //throws if myNewNullableInt == null
printAnInteger(it)
}
Because you are using a non-mutable val you can also use `if(myNewNullableInt != null). Kotlin will recognize the null-check and auto-cast to the non-nullable type. You can see the smart-cast by the green marking of AndroidStudio or Idea.
val myNewNullableInt: Int? =6
if(myNewNullableInt != null) {
printAnInteger(myNewNullableInt )
}
In java we can call a method aMethod(val1 , val2)
with classObject.aMethod(null_if_Val1_NotAvailable,val2)
And inside the Method i will check that if val1 is null
i will initialize that
How to do the same in Kotlin.
I tried but its saying Null Cannot be value of Non-null type
You can use default values:
fun aMethod(val1: String = "default value", val2: String)
You can now use the default method like so:
classObject.aMethod(val2 = "val2")
Try it this way
fun printHello(name: String?): Unit {
if (name != null)
println("Hello ${name}")
else
println("Hi there!")
// `return Unit` or `return` is optional
}
Return type as your wish.
Refrence from kotlin langauge function tutorial
? after the datatype allows you to have null. refer this link
Kotlin emphasis on null safety. If you want a property/return type/parameter/local variable to be null, you will have to use nullable types, by appending a question mark to the end of your type.
e.g.
Foo foo = null // doesn't compile
Foo? foo = null // compiles
fun foo(foo: Foo)
foo(null) // doesn't compile
fun bar(foo: Foo?)
bar(null) // compiles
fun foo(): Foo = null // doesn't compile
fun foo(): Foo? = null // compiles
So in order to make your code works, all you need is to specify that function aMethod takes a nullable type:
fun aMethod(val1: SomeType?, val2) {
//...
}
However, in kotlin, it's not suggested to use null here. We have a better way to deal with your situation. We use default parameters, as suggested by nhaarman.
fun aMethod(val1: SomeType = someDefaultValue, val2: OtherType) {
//...
}
aMethod(val2 = OtherType())
on Ruby one have something like this:
#var ||= 'value'
basically, it means that #var will be assigned 'value' only if #var is not assigned yet (e.g. if #var is nil)
I'm looking for the same on Kotlin, but so far, the closest thing would be the elvis operator. Is there something like that and I missed the documentation?
The shortest way I can think of is indeed using the elvis operator:
value = value ?: newValue
If you do this often, an alternative is to use a delegated property, which only stores the value if its null:
class Once<T> {
private var value: T? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): T? {
return value
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
this.value = this.value ?: value
}
}
You can now create a property that uses this like so:
var value by Once<String>()
fun main(args: Array<String>) {
println(value) // 'null'
value = "1"
println(value) // '1'
value = "2"
println(value) // '1'
}
Note that this is not thread-safe and does not allow setting back to null. Also, this does evaluate the new expression while the simple elvis operator version might not.
Other way we could do it is by using ifEmpty..
From the docs: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/if-empty.html
Usage:
val empty = ""
val emptyOrNull: String? = empty.ifEmpty { null }
println(emptyOrNull) // null
val emptyOrDefault = empty.ifEmpty { "default" }
println(emptyOrDefault) // default
val nonEmpty = "abc"
val sameString = nonEmpty.ifEmpty { "def" }
println(sameString) // abc
EDIT:
Seems like this does not work if the initial value is NULL and only for strings..