Smart cast impossible because of threading


#1

I'm porting a project of mine to Kotlin. I often receive errors of the form:

Smart cast to 'kotlin.Array<kotlin.Int>' is impossible, because 'offsets' is a member variable that can be changed from another thread

Because of this, I have !! assertions in a lot of places that shouldn't need it, which I don't like. Is there any way to tell the compiler that I'm disciplined about threads surrounding a particular type or variable, or that my program is single-threaded, or something else to bypass this error? If my program has a race condition, nulls are the least of my problems.


#2

No, there is no such way. If you could show more of your code, we could probably suggest a more idiomatic solution for this than putting in !!. For example, the "lateinit" modifier might be a solution for your problem.


#3

Here's a trimmed-down example:

class PlayerManager {
    private var curPlayer: SongPlayer? = null

  fun play(song: Song) {
  if (curPlayer != null) {
           curPlayer!!.release()
           // some other code that makes ?. not good enough
  }
  curPlayer = SongPlayer(song)
  curPlayer!!.prepare()
  curPlayer!!.playWhenReady = true
  }
}


This code is called from an Android service. Android only interacts with services on the app’s main thread, and no other threads exist which can access curPlayer. lateinit won’t work because the variable is set to null elsewhere when there is no song playing. Usually I can add an extra local var or two to get around the issue, but they tend to be distracting and make the code longer/noisier than it needs to be.

I understand the thought process behind flagging this as an error, but I also find it disappointing because it essentially nullifies (no pun intended) Kotlin’s safety guarantees any time mutable fields are involved. It would be nice not to have to bypass the type system so often.


#4

Something like this?

class PlayerManager {   private var curPlayer: SongPlayer? = null

  fun play(song: Song) {
  curPlayer?.apply {
           release()
           // some other code that makes ?. not good enough
  }
  curPlayer = SongPlayer(song).apply {
           prepare()
           playWhenReady = true
  }
  }
}


#5

Yeah, that's annoying isn't it. You can use local vals to work around it:

curPlayer?.release()

val newPlayer = SongPlayer()
newPlayer.prepare()
newPlayer.playWhenReady()
curPlayer = newPlayer


or use apply, as Mark showed.