【问题标题】:How to write strongly typed generic extension function in Kotlin?如何在 Kotlin 中编写强类型泛型扩展函数?
【发布时间】:2019-12-05 00:56:50
【问题描述】:

关注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)


原始上下文

尝试编写一个小型断言框架的练习。稍微复杂一点,但试图得到上面最简单的复制。

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")
        }
    }
}

【问题讨论】:

  • (问题从一开始就很清楚;我认为原始上下文除了大容量之外并没有增加太多。在链接中会更好,还是省略?)

标签: generics kotlin extension-methods type-inference


【解决方案1】:

这似乎是由于Collection 接口参数的差异,该参数定义为Collection&lt;out E&gt;

这意味着Collection&lt;Any&gt;Collection&lt;E&gt; 的超类型,因此(显然)可以在Collection&lt;Int&gt; 上调用Collection&lt;Any&gt;.myContains() 扩展。

您可以通过将其替换为不变的MutableCollection(以及将listOf() 替换为mutableListOf())来确认这一点;然后你会得到预期的编译时“类型不匹配”错误。

不过,这让我感到惊讶。我猜编译器必须使用接收器类型和参数类型来推断E。 (谁能证实这一点?)而且,正如您所指出的,它具有阻止更严格的类型安全性的恼人效果。

【讨论】:

  • 我想知道是否将它作为一个不变位置 (T@Asserter) 的上限 (T@contain) 有什么不同?!可能不会,因为它们的行为方式相同。除了原始上下文是更多级别的推断泛型?
【解决方案2】:

这看起来像是一个解决方案:

class Asserter<in T>(val value: @UnsafeVariance T)

【讨论】:

  • 这不会导致“这不应该编译”行错误:(
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多