关注 strong 和通用部分。
假设我具有此扩展功能:
fun <E> Collection<E>.myContains(item: E) : Boolean {
// quite pointless, I know, but a simple example
return item in this
}
目的是编写一个仅接受集合元素类型(E
)的函数,但是编译器未对其进行验证吗?!
val isItInside: Boolean = listOf(1, 2).myContains("1")
愉快地编译。我的猜测是E
被推断为Any
。
如何在Kotlin类型系统/泛型中强制执行此限制?
(Kotlin版本1.3.41)
尝试编写一个小的断言框架的练习。稍微复杂一点,但是尝试获得上面最简单的repro。
class Asserter<T>(val value: T)
infix fun <T> T.should(block: Asserter<T>.() -> Unit) =
Asserter(this).block()
fun <T : Collection<*>> Asserter<T>.haveSize(size: Int) {
check(this.value.size == size) {
"expecting ${this.value} to be of size $size"
}
}
fun <E, T : Collection<E>> Asserter<T>.contain(item: E) {
check(item in this.value) {
"$item does not exist in $item"
}
}
class ShouldTest {
@Test fun intList() {
listOf(1, 2) should {
haveSize(2)
contain(1)
contain("2") // this shouldn't compile
}
}
@Test fun stringList() {
listOf("1", "2") should {
haveSize(2)
contain(1) // this shouldn't compile
contain("2")
}
}
}
答案 0 :(得分:4)
这似乎是由于Collection<out E>
接口参数(定义为Collection<Any>
)上的差异所致。
这意味着Collection<E>
是Collection<Any>.myContains()
的超类型,因此(显然)Collection<Int>
扩展名可以在MutableCollection
上调用。
您可以通过将其替换为不变的listOf()
(以及将mutableListOf()
替换为E
)来确认这一点;您会按预期收到编译时“类型不匹配”错误。
这让我感到惊讶。我猜编译器必须同时使用接收器类型和参数类型来推断<!doctype html>
<html>
<head>
<meta charset='utf8'>
<meta name='viewport' content='width=device-width'>
<title>Svelte app</title>
<link rel='icon' type='image/png' href='favicon.png'>
<link rel='stylesheet' href='global.css'>
<link rel='stylesheet' href='bundle.css'>
</head>
<body>
<script src='bundle.js'></script>
</body>
</html>
。 (任何人都可以确认吗?)正如您所指出的那样,它具有阻止更严格的类型安全性的烦人作用。
答案 1 :(得分:0)
这看起来像一个解决方案:
class Asserter<in T>(val value: @UnsafeVariance T)