首页 > 解决方案 > Kotlin: Generic type functions: Out-projected type ... 禁止使用

问题描述

给定一个类:

class Data<T>{
    fun get(): T = Something as T
    fun update(item: T) { }
}

如何做这样的事情?

fun alterData(d: Data<*>){
    d.update(d.get())
}

函数“alterData”不会编译:

Out-projected type 'Data<*>' 禁止使用 'fun update(item: T)'

更新: alterData() 的调用者不知道类型,所以这是不可能的:

fun <T>alterData(d: Data<T>){
    d.update(d.get())
}

我发现绕过这个限制的唯一方法是在类中编写方法,但这会破坏抽象

标签: kotlingenerics

解决方案


*基本上意味着你不知道T在这种情况下是什么类型,所以它可以是任何东西。

因为你已经定义dData<*>调用d.get()返回Any?。编译器不知道d包含什么类型的数据。因此,您必须期待任何对 有效的东西T,包括null.

因为您已经定义dData<*>calld.update(…)只接受Nothing,即根本没有价值。编译器无法知道d允许接收什么类型的数据。因此它不能接受任何东西,因为它在这里可能有效也可能无效。

您想告诉编译器的是d(出投影)和进出(投影)之间的关系d

fun <T> alterData(d: Data<T>) {
    d.update(d.get())
}

现在编译器知道(通过)输出的d内容.get()与您放入d(通过.update(…))的内容相同。在这两种情况下,该类型都称为T.

请注意,“类型参数”T不必与您的Data<T>声明具有相同的名称。以下内容同样有效地表明了这种关系:

fun <Something> alterData(d: Data<Something>) {
    d.update(d.get())
}

如果您进一步分解代码,可能会更容易理解:

fun alterData(d: Data<*>) {
    // `value` is of type `Any?` because we don't know `T` of `Data`
    val value = d.get()

    // `update` rejects `Any?` b/c we don't know `T` and it may not be valid
    d.update(value)
}
fun <Something> alterData(d: Data<Something>) {
    // `value` is of type `Something` b/c we know that `T` of `Data` is `Something`
    val value = d.get()

    // `update` accepts `Something` b/c we know `T` is `Something`
    d.update(value)
}

在你的情况下,调用者和被调用者(alterData)都不知道你的实际类型,T你必须告诉编译器通过使用强制转换来忽略编译时类型安全。

您可以在调用者或被调用者中执行此操作。更有意义的地方取决于实际用例。通常调用者比被调用者更了解上下文和可能的类型T,因此在那里执行强制转换是有意义的。

fun foo(d: Data<*>) {
   // ignore all type safety for `T`
   alterData(d as Data<Any?>)
}

被调用者alterData立即将它从一个Data实例 ( d) 接收到的值传递回同一实例。在这种情况下,可以安全地假设这永远不会出错,因为您对class Data. 如果是这种情况,那么您可以安全地在alterData函数中执行案例:

fun alterData(d: Data<*>){
    // ignore type safety and allow any value to be passed into `d`
    (d as Data<Any?>).update(d.get())
}

这两种方法都会引发编译器警告,您可以使用@Suppress("UNCHECKED_CAST").


推荐阅读