假设我有一个班级
Message
和一个班级Channel<T : Message>
.
现在,为什么我不能在没有 Unchecked Cast 警告的情况下将
Channel<out Message>
投射到 Channel<Message>
?
这种转换不应该总是安全的吗,因为
Channel<out Message
只能包含 Message
类型的对象或 Message
类型的子类?
不,这是证据。
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
会导致错误。
不,将
out Message
转换为 Message
是不安全的。
Channel<out Message>
基本上意味着 Channel
被限制只能生产 Message
物品,它不能消耗它们,例如因为我们不知道它到底可以消耗什么类型。通过将其转换为Channel<Message>
,我们允许消耗Message
。编译器无法在编译时或运行时验证这是正确的,因此它会生成一个未经检查的转换警告。
如果
Channel
类型在你的情况下是为了只生产物品,那么它应该在声明网站上标记为out T
。然后我们可以简单地使用它Channel<Message>
.
为了完整性,让我添加到以前的答案中。您的问题有 2 个级别。
“未经检查的转换”警告与您尝试转换为/转换的类型无关。这是因为
Channel<X>
和 Channel<Y>
由于 type erasure 在运行时没有区别。因此,从一个到另一个的转换在运行时永远不会失败,即使它是错误的:它在运行时是未检查(因此是“未检查”)。这可能会导致细微的错误,因为您正在污染堆。
这在转换非泛型类型时有所不同,例如
Any
到 String
,或泛型类型的原始部分,例如 List<*>
到 MutableList<*>
。如果有问题的实例类型不正确,这些在运行时会正常失败。
这个转换不应该总是安全的吗,因为
只能包含消息类型的对象或Channel<out Message>
类型的子类?Message
实际上不,它不安全,正如其他答案所显示的那样。声明中的
out
阻止你向这个频道发送元素(它是out
-only,你不能放东西in
)。这可以防止您插入通道不支持的内容。转换会使编译器允许这样做,这在运行时是不正确的。