簡體   English   中英

Smartcast 是不可能的,因為屬性有開放或自定義的 getter

[英]Smartcast is impossible because property has open or custom getter

我正在學習Kotlin,我的代碼如下:

override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    decoupler.attachNotifier(this)
    if(activity is ScreenRouter) {
        decoupler.attachRouter(activity)
    }
}

attachRouter()方法:

 fun attachRouter(router: ScreenRouter?) {
    this.router = router
}

文檔中所寫,kotlin 在使用 is 運算符檢查后自動轉換為類型。 所以,我預計它會起作用。 但相反,編譯錯誤讓我感到困擾:

Smartcast 到ScreenRouter是不可能的,因為activity是一個具有開放或自定義 getter 的屬性。

我認為錯誤可能是因為活動可以為空所以我嘗試了:

if(activity!=null && activity is ScreenRouter) {
     decoupler.attachRouter(activity)
}

但它沒有用,編譯失敗並出現同樣的錯誤。

但是,以下代碼可以正常工作:

if(activity is ScreenRouter) {
    decoupler.attachRouter(activity as ScreenRouter)
}

沒關系,但上面的錯誤似乎並沒有解釋 smartcast 失敗的原因。 我不是Kotlin專家,我只是初學者學習Kotlin。我在任何地方都找不到文檔。 這些類型的錯誤描述讓 Kotlin 變得難以學習。 誰能簡單解釋一下?

這里的關鍵點是,不能保證open屬性或帶有自定義 getter 的屬性在連續調用時返回相同的值。

因此,編譯器無法確定,一旦檢查了從屬性接收到的值,就可以安全地假設如果再次調用它將返回相同的對象甚至相同類型的對象。

示例(雖然非常簡化和合成):

open class Base {
    open val value: List<Int> = ArrayList()
}

val b : Base = foo()

fun printArrayList(list: ArrayList<Int>) { /* ... */ }

if (b.value is ArrayList) { // first call
    printArrayList(b.value) // second call, smart cast is impossible
}

這段代碼不會編譯,因為printArrayList()需要一個ArrayList並且b.valueopen ——這就是你在代碼中得到的。 現在,讓我們組成一個派生類來演示可能出錯的地方:

class Derived : Base() {
    private var counter = 0

    override val value: List<Int>
        get() {
            ++counter
            return if (counter % 2 == 0)
                ArrayList() else
                LinkedList()
        }
}

val b = Derived()
println(b.value.javaClass) // class java.util.LinkedList
println(b.value.javaClass) // class java.util.ArrayList

在這里很明顯,如果一個屬性是open ,它可以通過連續調用返回不同值的方式被覆蓋。 在使用printArrayList()的示例中,有兩個這樣的調用。 這就是為什么聰明的演員不會安全。 對於具有自定義 getter 的屬性也是如此。

您在if塊內執行as -cast 的示例有效,因為如果該屬性在第二次調用時返回了不兼容類型的不同值,則轉換將失敗並拋出ClassCastException ,這將保持類型安全。

並且,相反,如果一個val屬性沒有open並且有一個默認的 getter 只返回支持字段的值(在這種情況下是final ),編譯器可以安全地執行智能轉換:如果你得到了屬性好幾次肯定是一樣的。


另一種方法是獲取一次值,將其存儲在局部變量中並多次使用它而不是再次使用該屬性:

val list = b.value

if (list is ArrayList) {
    printArrayList(list) // smart cast to ArrayList
}

現在,無論一個屬性是否為open ,只有一次調用它的 getter ,然后代碼使用調用返回的值進行操作。 由於它無法更改,因此可以在此處進行智能轉換。

而不是直接使用活動這是一個可為空的對象,我這樣做了

activity?.let{
   if(it is ScreenRouter) {
      decoupler.attachRouter(it)
   }
}

與此代碼片段沒有直接關系,但常見的情況是,如果ViewModel中有一個state定義如下:

    private val _state = MutableStateFlow<CardScreenState>(CardScreenState.Loading)
    val state: StateFlow<CardScreenState> = _state

然后當你在when子句中使用這個state

 when (state) {

你會得到同樣的錯誤。

作為解決方案,您可以使用不可變的 getter 創建一個 state 變量,如下所示:

    when (val screenState = state) {

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM