Kotlin';当contains()取E时,s集是协变的吗?
我在研究几种编程语言的集合库中的协变和逆变,无意中发现了Kotlin的接口 记录如下:Kotlin';当contains()取E时,s集是协变的吗?,kotlin,collections,covariance,Kotlin,Collections,Covariance,我在研究几种编程语言的集合库中的协变和逆变,无意中发现了Kotlin的接口 记录如下: interface Set<out E> : Collection<E> () 因此,Set不是Set的“真实”子类型,因为Set.contains(5)与第二个而不是第一个一起工作 实际上调用contains方法甚至可以在运行时工作——只是我的实现永远不会被调用,它只会打印false 查看接口的源代码,结果发现这两个方法实际上声明为 abstract fun contains(el
interface Set<out E> : Collection<E>
()
因此,Set
不是Set
的“真实”子类型,因为Set.contains(5)
与第二个而不是第一个一起工作
实际上调用contains方法甚至可以在运行时工作——只是我的实现永远不会被调用,它只会打印false
查看接口的源代码,结果发现这两个方法实际上声明为
abstract fun contains(element: @UnsafeVariance E): Boolean
abstract fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean
abstract fun contains(元素:@UnsafeVariance):布尔值
抽象元素(元素:集合):布尔值
这是怎么回事?
是否有一些特殊的编译器魔术集?
为什么没有在任何地方记录这一点?以
out
修饰符的形式声明站点协方差忽略了一个有用的用例,即确保作为参数传递的实例通常可以在此处传递。包含
函数就是一个很好的例子
在Set.contains
的特殊情况下,@UnsafeVariance
注释用于确保函数接受E
的实例,因为将不属于E
的元素
传递到contains
中毫无意义–所有Set
的正确实现都将始终返回false
。Set
的实现不应存储传递给contains
的元素
,因此不应使用返回类型E
从任何其他函数返回该元素。因此,正确实现的集
在运行时不会违反方差限制
@UnsafeVariance
注释实际上会抑制编译器差异冲突,就像在in
-位置使用out
-投影类型参数一样
其动机最好描述在:
@UnsafeVariance
注释
有时我们需要在类中禁止声明站点差异检查。例如,要使Set.contains
typesafe同时保持只读集的共变量,我们必须执行以下操作:
interface Set<out E> : Collection<E> {
fun contains(element: @UnsafeVariance E): Boolean
}
接口集:集合{
乐趣包含(元素:@UnsafeVariance):布尔值
}
这就给包含的的实现者带来了一些责任,因为通过抑制此检查,元素的实际类型在运行时可能是任何类型,但有时需要实现方便的签名。请参阅下面有关集合类型安全性的更多信息
为此,我们在类型上引入了@UnsafeVariance
注释。它被故意制作了很长时间,并站出来警告滥用它的人
博客文章的其余部分还明确提到,的签名包含使用@UnsafeVariance
提高类型安全性的
引入@UnsafeVariance
的替代方案是保持包含接受任何,但是这个选项缺少对包含的调用的类型检查,该调用将检测到元素的错误调用,这些调用由于不是E
的实例而无法出现在集合中,但是我的实现怎么连这个参数都没有被调用呢?另外,我喜欢“contains的文档中甚至没有提到“contains的实现者的责任”。@Paŭloeberman是的,这似乎涉及到一些编译器魔法。如果您检查为StringSet
实现生成的字节码,您会发现桥接方法包含(Ljava/lang/Object;)Z
检查元素
是否为字符串
,如果不是,则立即返回false,而不调用重写的实现。请参阅。因此,我猜contains的实现者的某些责任现在由编译器为内置集合接口处理。但是,我猜这是错误的签名中带有@UnsafeVariance
的自定义类型未处理。让我检查一下。在包含所有的情况下,实现者仍有责任准确处理参数,因为在这种情况下,桥接器无法检查已擦除集合参数的实际类型。
abstract fun contains(element: @UnsafeVariance E): Boolean
abstract fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean
interface Set<out E> : Collection<E> {
fun contains(element: @UnsafeVariance E): Boolean
}