如何在Kotlin中编写强类型泛型扩展函数?

TWi*_*Rob 5 generics extension-methods type-inference kotlin

专注于强大通用的部分。

假设我有这个扩展功能:

fun <E> Collection<E>.myContains(item: E) : Boolean {
    // quite pointless, I know, but a simple example
    return item in this
}
Run Code Online (Sandbox Code Playgroud)

目的是编写一个仅接受集合元素(E)类型的函数,但是编译器未对此进行验证?

val isItInside: Boolean = listOf(1, 2).myContains("1")
Run Code Online (Sandbox Code Playgroud)

愉快地编译。我的猜测是,E被推断为Any

如何在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")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

gid*_*dds 4

这似乎是由于Collection接口参数的差异所致,该参数定义为Collection<out E>

\n\n

这意味着它Collection<Any>是 的超类型Collection<E>,因此(显然)Collection<Any>.myContains()可以在Collection<Int>.

\n\n

您可以通过将其替换为不变量MutableCollection(以及listOf()withmutableListOf())来确认这一点;然后,您将收到预期的编译时 \xe2\x80\x98Type Mismatch\xe2\x80\x99 错误。

\n\n

不过,这让我感到惊讶。\xc2\xa0 我猜编译器必须E使用接收器类型和参数类型来推断。\xc2\xa0 (任何人都可以确认这一点吗?)\xc2\xa0 并且,正如您所指出的,它有阻止更严格的类型安全带来的恼人的影响。

\n