Different null check behavior in assignment vs expression

Hello! Recently I was having a discussion here about null coalescing operators used on a nullable property, and it brought up some strange behavior with regards to assignments/declarations.

(Note: I’ve never had an issue with this in my own code, just an interesting case.)

if I set up everything like this, with the goal of using primary.id or else alt.id or else null:

class Test(val id: String? = null)
var primary: Test? = null 
primary = Test() 
var alt: Test? = null 
alt = Test("alternate")

This works as expected:

var id: String? = null
id = primary?.id ?: alt?.id 
println(id) // prints "alternate"

But if the id is set in the declaration no dice:

var id: String? = primary?.id ?: alt?.id
println(id) // always prints "null" (id property of primary)

Same deal with an if statement:

// Always null
var id: String? = if (primary?.id != null) primary!!.id else alt?.id 

// Works as intended
var id: String? = null
id = if (primary?.id != null) primary!!.id else alt?.id

The difference seems to be caused by whether something is an assignment or an expression, I’m curious about what is going on under the hood here.

2 Likes

These all print “alternate” from what I can observe:

class Test(val id: String? = null)

fun main(){
    var primary: Test? = null 
	primary = Test() 
	var alt: Test? = null 
	alt = Test("alternate")
    var id1: String? = null
	id1 = primary?.id ?: alt?.id 
	println(id1)
    var id2: String? = primary?.id ?: alt?.id
	println(id2)
	var id3: String? = if (primary?.id != null) primary!!.id else alt?.id 
	println(id3)
	var id4: String? = null
	id4 = if (primary?.id != null) primary!!.id else alt?.id
    println(id4)
}

Do you maybe have an example that reproduces this bug?

Now that I run it in it’s own file with a proper main() function it works fine, and with nice smart casting as well so the safe calls aren’t needed. :slight_smile:

When I was encountering the issue, I was running the examples above almost verbatim in a scratch file in intellij, so it might be a difference in context having to do with scripting vs running a project.

1 Like

scratch files usually have really really weird issues. I think maybe what happened here is that it defined all the variables with their initialisers first and then ran the rest of the code, and so the initialiser was evaluating to the null values that you had in all the other variables