为什么Kotlin在将协变类型参数强制转换为不变类型参数时会产生Unchecked Cast警告?

hlswsv35  于 2023-03-19  发布在  Kotlin
关注(0)|答案(3)|浏览(717)

假设我有一个类Message和一个类Channel<T : Message>
现在,为什么我不能将Channel<out Message>施法到Channel<Message>,而没有未检查施法警告?
既然Channel<out Message只能包含Message类型的对象或Message类型的子类,这个强制转换不是应该总是安全的吗?

x3naxklr

x3naxklr1#

不,这就是证据。

class Channel<T : Message> {
  fun send(t: T)
  fun receive(): T
}
class Apple : Message()
class AppleChannel : Channel<Apple>() {
  ...
}

val appleChannel = AppleChannel()
val outMessageChannel: Channel<out Message> = appleChannel // MUST WORK
val messageChannel: Channel<Message> = outMessageChannel 
   // you say this should work, but...
messageChannel.send(Orange()) 
   // sending orange on a channel that only knows how to send apples!  
   // Error!

在这种情况下,唯一一致的做法是不允许将Channel<out Message>转换为Channel<Message>
如果它被定义为Channel<out T: Message>,那么这将是安全的,但是尝试定义send将导致错误。

tzcvj98z

tzcvj98z2#

不,将out Message转换为Message是不安全的。
Channel<out Message>基本上意味着Channel被限制为只能生成Message项,它不能使用它们,例如,因为我们不知道它到底可以使用什么类型。通过将它强制转换为Channel<Message>,我们允许使用Message。编译器无法在编译时或运行时验证这是否正确,因此它生成一个未检查的强制转换警告。
如果Channel类型在你的例子中只用于生成元素,那么它应该在声明位置标记为out T,然后我们可以简单地使用它。

rkkpypqq

rkkpypqq3#

让我补充一下前面的答案,你的问题有两个层次。
“unchecked cast”警告不是关于你试图转换的类型,而是关于Channel<X>Channel<Y>在运行时没有区别的事实,因为type erasure。所以从一个类型到另一个类型的转换在运行时永远不会失败,即使它是错误的:它在运行时没有被检查(因此是“unchecked”),这可能会导致一些细微的错误,因为你是polluting the heap
这在强制转换非泛型类型(如AnyString)或泛型类型的原始部分(如List<*>MutableList<*>)时是不同的。如果所讨论的示例不是正确的类型,这些类型在运行时会失败。
既然Channel<out Message>只能包含Message类型的对象或Message类型的子类,那么这种类型转换不应该总是安全的吗?
实际上不,正如其他答案所示,这是不安全的。声明中的out防止您将元素发送到该通道(它是out-only,您不能将内容放入in)。这防止您插入通道不支持的内容。强制转换将使编译器允许这样做,这在运行时是不正确的。

相关问题