Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin "Smart cast is impossible, because the property could have been changed by this time"

Tags:

android

kotlin

Why Android Studio show error when I use No.2 script. I found no different between 1 and 2.

class Adapter {
    var nameList : ArrayList<String>? = null
}

class Program {
    private fun send() {
        val list: ArrayList<String> = ArrayList()
        val adapter = Adapter()

// Case 1
        var otherList = adapter.nameList
        if (otherList != null) {
            list.addAll(otherList) // <--- no error
        }

// Case 2
        if (adapter.nameList!=null) {
            list.addAll(adapter.nameList) // <--- Error here
            // Smart cast to 'kotlin.collections.ArrayList<String> /* = java.util.ArrayList<String> */' is impossible, because 'adapter.nameList' is a mutable property that could have been changed by this time
        }
    }
}

Please explain this case

like image 832
quangkid Avatar asked Oct 12 '17 03:10

quangkid


2 Answers

The IDE should give you a warning, explaining that after the null check, it's possible that adapter.nameList was changed by another thread, and that when you call list.addAll(adapter.nameList), adapter.nameList could actually be null by that point (again, because a different thread could have changed the value. This would be a race condition).

You have a few solutions:

  1. Make nameList a val, which makes its reference final. Since it's final, it's guaranteed another thread can't change it. This probably doesn't fit your use case.

    class Adapter {
        val nameList : ArrayList<String>? = null
    }
    
  2. Create a local copy of name list before you do the check. Because it's a local copy, the compiler knows that another thread can't access it, and thus it can't be changed. The local copy could be defined with either a var or a val in this case, but I recommend val.

    val nameList = adapter.nameList
    if (nameList != null) {
        list.addAll(nameList)
    }
    
  3. Use one of the utility functions that Kotlin provides for just such a case as this. The let function copies the reference it's called on as a parameter using an inline function. This means that it effectively compiles down to be the same as #2, but it's a bit more terse. I prefer this solution.

    adapter.nameList?.let { list.addAll(it) }
    
like image 96
spierce7 Avatar answered Oct 14 '22 14:10

spierce7


Your adapter.nameList is mutable property so please convert it to immutable.

Use this

  val nameList : ArrayList<String>? = null

Instead of this

  var nameList : ArrayList<String>? = null

Or you can also solve this problem by assert of non null Assert

            list.addAll(adapter.nameList!!)

Note :- !! is evaluated at runtime, it's just an operator.

The expression (x!!)

throws a KotlinNullPointerException if x == null, otherwise, it returns x cast to the corresponding non-nullable type (for example, it returns it as a String when called on a variable with type String?).

like image 5
vishal jangid Avatar answered Oct 14 '22 15:10

vishal jangid