java - Smartcast 是不可能的,因为属性有开放的或自定义的 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
}

如 documentation 中所述, kotlin 在使用 is 运算符检查后自动转换为类型。所以,我希望它会起作用。但相反,编译错误让我感到困扰:

智能转换到 ScreenRouter 是不可能的,因为 activity 是一个具有开放或自定义 getter 的属性。

我认为错误可能是因为 Activity 可以为空,所以我尝试了:

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

但它不起作用,编译失败并出现同样的错误。

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

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

没关系,但上述错误似乎无法解释智能广播失败的原因。我不是 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() 的示例中,有两个这样的调用。这就是智能 Actor 不安全的原因。具有自定义 getter 的属性也是如此。

您在 if block 内执行 as-cast 的示例有效,因为如果属性返回,该转换将失败并抛出 ClassCastException在第二次调用时使用不兼容类型的不同值,这将保持类型安全。

相反,如果一个 val 属性不是 open 并且有一个默认的 getter,它只返回 backing field 的值。 (在这种情况下是 final),编译器可以安全地执行智能转换:如果多次获取属性的值,它肯定是相同的。


另一种方法是获取一次值,将其存储在局部变量中并多次使用它,而不是再次使用该属性:

val list = b.value

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

现在,无论一个属性是否open,只有一次调用它的getter,然后代码使用调用返回的值进行操作。既然不能改变,这里就可以智能施法了。

https://stackoverflow.com/questions/41086296/

相关文章:

android - 带有命名参数的 Kotlin 中的 Dagger 2 构造函数注入(inject

java - 安卓3.3.0更新,错误: Cause: invalid type code: 68

java - 在 Kotlin 中使用 Room 的 @ForeignKey 作为 @Entity

hibernate - 如何使用 JpaRepository 进行批量(多行)插入?

android - 抑制 "Identifier not allowed in Android"

java - getActionView 已弃用?

kotlin - 我可以省略在 Kotlin 中不使用的接口(interface)方法吗?

kotlin - 如何使用 Gradle Kotlin 脚本创建胖 JAR?

intellij-idea - 为什么 IntelliJ 建议将调用链转换为序列?

android - Kotlin Android 打印到控制台