Smartcast是不可能的,因为propery有开放或自定义的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在与操作员核对之后自动进行类型转换。 所以,我预计它会工作。 但相反,它困扰我编译错误说:

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()需要一个ArrayListb.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并抛出一个ClassCastException ,这将保持类型安全。

而相反,如果一个val属性没有open并且有一个默认的getter,它只是返回后台字段的值(在这种情况下是final ),编译器就可以安全的执行一次smart cast:如果你得到该财产几次肯定是相同的。


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

 val list = b.value if (list is ArrayList) { printArrayList(list) // smart cast to ArrayList } 

现在,无论某个属性是否open ,只有一个调用它的getter,然后这个代码就会调用返回的值。 既然不能改变,那么在这里可以做个聪明的演员。