Elvis Operator vs Non-Null Assertion: Diff Between These Statements? - nullpointerexception

Trying to understand null safety in Kotlin: both of these following statements seem to work fine and be interchangeable in my program:
var y1: Double = 0.0
get() = when(hasParent) {
true -> parent!!.y1
else -> field
}
and
var y1: Double = 0.0
get() = parent?.y1!!
(hasParent is simply a getter to see if parent is null or not)
Are these two semantically the same or are they actually different expressions that mean different things? And if they mean the same thing semantically, is the first ever preferred over the second for any reason?

In this case you don't need hasParent. The form which is applicable is this:\
var y1: Double = 0.0
get() = parent?.y1 ?: field
The problem with your second getter is that it will try to return the parent.y1 no matter what and it will throw a NullPointerException if it is not there.
If you use IDEA you'll get warnings for these kinds of problems so they are an easy fix, but you need to be aware of the so called platform types which come from Java code:
val queue: Queue<String> = LinkedList()
queue.peek().toInt()
Here the compiler won't complain for .toInt() although it is possible to get a null from the Queue. I've written about this here.

Note that jingx's and Adam Arold's answer is good for the current situation, but it wouldn't be equivalent to your first snippet if the type of y1 was nullable: if parent is not null, but parent.y1 is, then your code gives null and parent?.y1 ?: field gives field.
I'd say the actual preferred form if you need the first behavior is
if (parent != null) parent.y1 else field // if parent is val without a custom getter
parent.let { if (it != null) it.y1 else field } // otherwise
Using hasParent hides from the compiler that you actually checked for null and don't need !!.

Related

Getters cannot be used to identify return type properly in Kotlin

I have a data class that has the following form:
data class ContentElementField(val type: String) {
val text: String? = null
get() = requireNotNull(field)
val style: String? = null
get() = requireNotNull(field)
val path: String? = null
get() = requireNotNull(field)
val caption: String? = null
get() = requireNotNull(field)
}
The problem arises when I want to perform the following operation:
when (it.type) {
"text" -> TextElement(Text(it.text), Style(it.style))
"image" -> ImageElement(Path(it.path), Caption(it.caption))
}
The compiler warns me about that You cannot send a nullable type to a function that does not accept nullable arguments.
Even if the field is signed to be nullable, its getter is signed to be not nullable, though.
The compiler should use getters to resolve whether to give this warning.
What would you offer to get around this problem?
It doesn't matter if your getter happens to crash if the current value is null - the type is still nullable, the getter's return type is still String?.
Why are you doing this anyway? Why not just make the fields non-null as normal and let a null assignment throw the exception instead? That way you won't have to fight the type system.
If what you have in mind is different and this was just meant to be a simple example, then you have a few options:
Use !! at the call site since you're guaranteeing it's not null
"text" -> TextElement(Text(it.text!!), Style(it.style))
Expose the private nullable property through a non-null one:
// I see people do this a lot in Activities and Fragments even though
// they should probably just be making the one property lateinit instead
private val _text: String? = whatever
val text: String get() = requireNotNull(_text)
Maybe look at Kotlin contracts which allow you to make guarantees to the compiler about values (no example because I've never used it)
It's not really clear what you actually want to do though, or why this is useful. Your example is even using vals and assigning null to them. Whatever your real use case is, there's probably a better way.
(Also in case you're not aware, properties that aren't constructor parameters aren't included in the basic data class behaviour, i.e. its equals/hashCode/toString implementations. Another reason just making the types non-null helps, you can stick them in the constructor instead of having to do this logic)

Kotlin multiple variable let, using the previous variables in the next let

I want to be able to have multiple let and the next let is able to use the previous variable only if its not null. The reason I want this is because I want only one :? for all the lets. Is this possible?
Example on how I want it to be:
fun example() {
firstVariable?.let a -> &&
exampleFunction(a, 3)?.let { a, b ->
// Use a and b freely since they are not null and not optionals
} ?: run {
// Runs if either one of the let is failing
}
}
// a is NOT optional
fun exampleFunction(a: Int, b: Int?): Int? {
if (b == null) {
return null
}
return a + b
}
Stupid example, but its just to show what I need... I want to check if the first variable is null AND to run a function that returns an optional with a non-optional parameter which is the first variable - if either of these fail, I want to run something else.
I know how to do this without let, but I am wondering if it's possible or planned to be able to do this? (It's possible in Swift).
How to do it in Swift:
// First check a, then use a in the same expression if its validated
if let a = firstVariable,
let b = exampleFunction(a) {
// Use a and b as non-optionals
} else {
// a or b failed
}
You propably missunderstood how let works. I am going to explain a bit. In short the desired behaviour is not possible in kotlin or at least you can not idiomatically emulate it without any drawbacks whatsoever.
I don't know swift but it seems as if the let used there is some sort of syntax construct offered by the language itself. It allows you to define a variable with some local scope and can be chained (like the short circuiting &&).
In Kotlin however let is just a normal function.
See the documentation. It's basically nothing more than
fun <T, R> T.let(block: (T) -> R): R = block(this)
It allows to call a function with a normal parameter as a function with a receiver type.
The actual null check is done with the ?. operator.
It takes an optional/nullable value as left hand side operand and either short circuits returning null or call the function on the right hand side with the non-null left hand side as receiver type. let is just one possible function to call here.
The similar ?: operator takes an optional/nullable LHS operand and returns this value if it is not null or it evaluates the expression on the RHS.
One way to get those variables defined is by nesting lets:
firstVariable?.let{a -> exampleFunction(a, 3)?.let{b -> a + b}} ?: run{}
where a + b is just an example of using both values. This however becomes unhandy if it's longer than one line. If you still want to define local variables you can create a block with run and use jump statements on the right side of ?:
run {
val a = firstValue ?: return#run null
val b = exampleFunction(a, 3) ?: return#run null
return#run a + b
} ?: run{}
While the above code looks really ugly with all those return#run null repititions there might be ways to reduce the amount of repeating code e.g. by using a anonymous function (to get rid of the #run part) or return Unit and safe the last value with some side-effect operation. (to get rid of the null and the last return statement)
You could benefit on Kotlin and write sot of extension function for your case. vararg as we dont know how many variables we want to pass, then check if all of them are not null and if so, return all of them. If any of the vars will be null, then nothing happens.
fun <T: Any> multipleLetCheck(vararg variables: T?, block: (List<T>) -> Unit): Unit? {
return if (variables.all { variable -> variable != null }) {
block(variables.filterNotNull())
} else {
null
}
}
// usage
multipleLetCheck(firstVariable, 3){ (firstVariable, secondVariable) ->
// work with firstVariable and secondVariable
} ?: run {
}

Is there an elegant kotlin way of convincing the compiler that a nullable field to which I just assigned a real value can't be null anymore?

I have read that using !! should generally be avoided.
Is there a way to write the following code in a more elegant way without having to add something like obsolete null checks and duplicated or dead blocks of code?
class A(var field: Thing?) {
fun getField(): Thing {
if (field == null) {
field = Thing()
}
return field!!
}
}
Also I don't understand why the compiler requires the !!-'pray-this-isn't-null-operator' to be satisfied in this scenario.
EDIT: Consider that it is important to me that a potential solution uses lazy initialization if the field is null!
Problem
As Enzokie already mentioned in the comments, another thread could have changed field after the null check. The compiler has no way of knowing that, so you have to tell it.
class A(var field: Thing?) {
fun getField(): Thing {
if (field == null) {
field = Thing()
}
// another thread could have assigned null to field
return field!! // tell the compiler: I am sure that did not happen
}
}
Solution (Eager)
In you particular case it would be a good idea to use a parameter f (you could name it "field" too, but I avoided that for clarity) in the constructor (without val/var) and afterwards assign it to a property field to which you assign either f or a new instance of Thing.
This can be expressed really concise with the Elvis operator :? which takes the left hand side if not null and the right hand side of the expression otherwise. So, in the end field will be of type Thing.
class A(f: Thing?) {
val field = f ?: Thing() // inferred type Thing
}
Solution (Lazy)
Since it was mentioned by gidds, if you need to initialize field lazyly you could do it like this using delegated properties:
class A(f: Thing?) {
val field by lazy {
f ?: Thing() // inferred type Thing
}
}
The call site does not change:
val a = A(null) // field won't be initialized after this line...
a.field // ... but after this
How about this?
class A(field: Thing?) {
private lateinit var field: Thing
init {
field?.let { this.field = it }
}
fun getField(): Thing {
if (!this::field.isInitialized) {
field = Thing()
}
return field
}
}
When you define a field, you actually define a variable plus two accessor methods:
val counter: Integer = 0
It is possible to customize the accessor methods by writing this instead:
val n = 0
val counter: Integer
get() = n++
This will execute the n++ each time you access the counter field, which therefore returns different values on each access. It is uncommon and unexpected but technically possible.
Therefore the Kotlin compiler cannot assume that two accesses to the same field return the same value twice. Usually they do, but it is not guaranteed.
To work around this, you can read the field once by copying it into a local variable:
fun count() {
val counter = counter
println("The counter is $counter, and it is still $counter.")
}

Is the a way to use the default value of a non-nullable parameter when null is passed as an argument?

I looking for a way to have default values take the place of nulls when passed as arguments. My motivation is purely to reduce the amount of code written (I want to avoid having overloaded functions/constructors or manual 'if null' checks)
My use case is within a Spring RestController, I want default values of a method called by the controller to be used without needing to state those default values outside the function.
I thought perhaps that using named parameters might provide this functionality but my experiments show otherwise. Perhaps there is a way with the elvis operator?
Example Code:
fun someFunction(first: Long = 1, second: Int = 2 ) {
// Do something
}
#GetMapping
fun someEndpoint(#RequestParam("first") firstParam: Long?):ResponseEntity<Any> {
someFunction(firstParam) // Attempt 1: "Required: Long\n Found: Long?
someFunction(first = firstParam) // Attempt 2: Same error
}
Hopefully you can help
There aren't any specific language features that would do this for you, the default argument mechanism isn't connected to nullability in any way.
However, you can achieve this in a more manual fashion by making your parameters nullable, and immediately substituting default values inside the function if they're null:
fun someFunction(first: Long? = null, second: Int? = null) {
val actualFirst: Long = first ?: 1
val actualSecond: Int = second ?: 2
// Do something with actualFirst and actualSecond
}
The #RequestParam annotation has a default value option named "defaultValue".
you can use it like so:
#GetMapping
fun someEndpoint(#RequestParam(name = "first", defaultValue = "1") firstParam: Long):ResponseEntity<Any> {
someFunction(firstParam) // firstParam equals to 1 if null was passed to the endpoint
}

Compilation error: Smart cast to '<type>' is impossible, because '<variable>' is a local variable that is captured by a changing closure

To simplify my real use case, let's suppose that I want to find the maximum number in a list:
var max : Int? = null
listOf(1, 2, 3).forEach {
if (max == null || it > max) {
max = it
}
}
However, compilation fails with the following error:
Smart cast to 'Int' is impossible, because 'max' is a local variable that is captured by a changing closure
Why does a changing closure prevent smart cast from working in this example?
In general, when a mutable variable is captured in a lambda function closure, smart casts are not applicable to that variable, both inside the lambda and in the declaring scope after the lambda was created.
It's because the function may escape from its enclosing scope and may be executed later in a different context, possibly multiple times and possibly in parallel. As an example, consider a hypothetical function List.forEachInParallel { ... }, which executes the given lambda function for each element of the list, but in parallel.
The compiler must generate code that will remain correct even in that severe case, so it doesn't make an assumption that the value of variable remains unchanged after the null check and thus cannot smart cast it.
However, List.forEach is quite different, because it is an inline function. The body of an inline function and the bodies of its functional parameters (unless the parameter has noinline or crossinline modifiers) are inlined at the call site, so the compiler could reason about the code in a lambda passed as an argument to inline function as if it was written directly in the calling method body making the smart cast possible.
It could, but currently, it doesn't. Simply because that feature is not implemented yet. There is an open issue for it: KT-7186.
Thanks to Ilya for the detailed explanation of the problem!
You can use the standard for(item in list){...} expression like this:
var max : Int? = null
val list = listOf(1, 2, 3)
for(item in list){
if (max == null || item > max) {
max = item
}
}
This looks like a compiler bug to me.
If the inline lambda parameter in forEach were marked as crossinline then I would expect a compilation error because of the possibility of concurrent invocations of the lambda expression.
Consider the following forEach implementation:
inline fun <T> Iterable<T>.forEach(crossinline action: (T) -> Unit): Unit {
val executorService: ExecutorService = ForkJoinPool.commonPool()
val futures = map { element -> executorService.submit { action(element) } }
futures.forEach { future -> future.get() }
}
The above implementation would fail to compile without crossinline modifier. Without it, the lambda may contain non-local returns which means it cannot be used in a concurrent fashion.
I suggest creating an issue: Kotlin (KT) | YouTrack.
The problem is that foreach creates multiple closures, each of which access the same max which is a var.
What should happen if max were set to null in another of the closures after the max == null check but before it > max?
Since each closure can theoretically work independently (potentially on multiple threads) but all access the same max, you can't guarantee it won't change during execution.
As this is in the top results for the error Smart cast to '<type>' is impossible, because '<variable>' is a local variable that is captured by a changing closure here is a general solution that worked for me (even if the closure is not inlined):
Sample showing this error:
var lastLine: String? = null
File("filename").useLines {
lastLine = it.toList().last()
}
if(lastLine != null) {
println(lastLine.length) // error! lastLine is captured by the useLines closure above
}
Fix: Create a new variable that is not captured by the closure:
var lastLine: String? = null
File("filename").useLines {
lastLine = it.toList().last()
}
val finalLastLine = lastLine
if(finalLastLine != null) {
println(finalLastLine.length)
}