Smartcast是不可能的,因为propery有开放或定制的吸气剂

Kru*_*hah 25 java android types casting kotlin

我正在学习Kotlin.我的代码如下:

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

attachRouter() 方法:

 fun attachRouter(router: ScreenRouter?) {
    this.router = router
}
Run Code Online (Sandbox Code Playgroud)

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

Smartcast ScreenRouter是不可能的,因为它activity是一个开放或定制的吸气剂.

我想也许错误是因为活动可以为空,所以我试过:

if(activity!=null && activity is ScreenRouter) {
     decoupler.attachRouter(activity)
}
Run Code Online (Sandbox Code Playgroud)

但它没有工作,编译失败同样的错误.

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

if(activity is ScreenRouter) {
    decoupler.attachRouter(activity as ScreenRouter)
}
Run Code Online (Sandbox Code Playgroud)

它没关系,但上面的错误似乎没有解释为什么smartcast失败.我不是Kotlin专家,我只是一个学习Kotlin的初学者.我找不到任何文件.这些错误描述使Kotlin难以学习.任何人都可以简单地解释一下吗

hot*_*key 29

这里的关键点是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
}
Run Code Online (Sandbox Code Playgroud)

这段代码不会编译,因为printArrayList()期望a ArrayListb.valueis open- 这就是你在代码中获得的代码.现在,让我们组成一个派生类,演示可能出现的问题:

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
Run Code Online (Sandbox Code Playgroud)

这里很明显,如果属性是open,则可以以对其连续调用返回不同值的方式覆盖它.在示例中printArrayList()有两个这样的调用.这就是智能演员不安全的原因.具有自定义getter的属性也是如此.

asif块内执行-cast的示例有效,因为转换会失败并抛出一个ClassCastException属性,如果属性在第二次调用时返回了不兼容类型的不同值,这将保留类型安全性.

而且,相反,如果val属性不是open并且具有默认的getter,它只返回支持字段的值(final在这种情况下),编译器可以安全地执行智能转换:如果您获得属性的值几个时间肯定是一样的.


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

val list = b.value

if (list is ArrayList) {
    printArrayList(list) // smart cast to ArrayList
}
Run Code Online (Sandbox Code Playgroud)

现在,无论属性是什么open,只有一个调用它的getter,然后代码使用调用返回的值进行操作.由于它无法改变,因此可以在这里进行智能投射.

  • 一个更实际的例子是“记住”的使用。`remember` 返回的任何内容都不再能够“智能转换”。但这可以通过创建记住值的副本来解决。复制的值可以“智能转换”。 (3认同)

kon*_*dev 29

与此代码片段没有直接关系,但常见情况是ViewModel内部的状态定义如下:

    private val _state = MutableStateFlow<CardScreenState>(CardScreenState.Loading)
    val state: StateFlow<CardScreenState> = _state
Run Code Online (Sandbox Code Playgroud)

然后当你在when子句中使用这个状态时

 when (state) {
Run Code Online (Sandbox Code Playgroud)

你会得到同样的错误。

作为解决方案,您可以使用不可变的 getter 创建一个状态变量,如下所示:

    when (val screenState = state) {
Run Code Online (Sandbox Code Playgroud)


Shy*_*der 7

而不是直接使用活动这是一个可为空的对象,我这样做了

activity?.let{
   if(it is ScreenRouter) {
      decoupler.attachRouter(it)
   }
}
Run Code Online (Sandbox Code Playgroud)