【问题标题】:Kotlin: Generic type functions: Out-projected type ... prohibits the use ofKotlin: Generic type functions: Out-projected type ... 禁止使用
【发布时间】:2020-08-07 23:13:26
【问题描述】:

给定一个类:

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

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

【问题讨论】:

    标签: kotlin generics


    【解决方案1】:

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

    因为您已将d 定义为Data&lt;*&gt;,所以调用d.get() 返回Any?。编译器无法知道d 包含什么类型的数据。因此你必须期待任何对T 有效的东西,包括null

    因为您已将d 定义为Data&lt;*&gt;,所以调用d.update(…) 只接受Nothing,即没有任何价值。编译器无法知道d 允许接收什么类型的数据。因此它不能接受任何东西,因为它在这里可能有效也可能无效。

    您想告诉编译器的是,d 的输出(投影外)和 d 的输入(投影)之间存在关系:

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

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

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

    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") 忽略该警告。

    【讨论】:

    • 没关系,很好解释,但调用者不知道类型,它只需要从类接收一些值并调用同一类的另一个提供值的方法,我更新了澄清问题
    • 感谢您提供更多信息。我已经相应地扩展了我的答案。
    猜你喜欢
    • 1970-01-01
    • 2018-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-15
    • 2011-03-18
    • 2015-12-08
    • 1970-01-01
    相关资源
    最近更新 更多