generics - Kotlin 泛型 : counterintuitive type inference and checking with out keyword

标签 generics kotlin covariant generic-variance

我最近在学习 Kotlin,同时有一些关于协变类型的问题。

示例代码在这里。 我有OptionOption2两者都有一个类型参数 T和一个 run扩展名。

我能理解前两个runvalidation() ,因为它们的行为与 Java 相同。 但是为什么第三行可以编译呢? Option<T>T不变 。我们无法通过Option<C>实例进入 where Option<B>预计。

添加 out 后关键字 T ,现在它们都可以编译了。为什么?

open class A
open class B : A()
open class C : B()


class Option<T>(val item: T)

fun <T> Option<T>.run(func: (Int) -> Option<T>): Option<T> = func(1)


class Option1<out T>(val item: T) //out keyword

fun <T> Option1<T>.run(func: (Int) -> Option1<T>): Option1<T> = func(1)


fun validation() {
    val opt: Option<B> = Option(B())
    opt.run { Option(A()) } //won't compile as expected
    opt.run { Option(B()) } //return type is Option<B>
    opt.run { Option(C()) } //return type is Option<B>; why could this compile?

    val opt1: Option1<B> = Option1(B())
    opt1.run { Option1(A()) } //return type is Option<A>; why could this compile?
    opt1.run { Option1(B()) } //return type is Option<B>
    opt1.run { Option1(C()) } //return type is Option<B>
}

最佳答案

  • opt.run { Option(C()) } //return type is Option<B>; why could this compile?

    在这里,您可以通过将调用分解为分别进行类型检查的两行来近似行为:

    val func: (Int) -> Option<B> = { Option(C()) }
    opt.run(func)
    

    第一行是正确的,因为:

    • lambda 预计返回 Option<B> (恰好 B ,因为 Option 是不变的),
    • 所以Option(item: T): Option<T>构造函数调用需要接受 B ,
    • 传递的参数是 C() ,
    • C : B , C()通过检查为 B ,
    • 等等Option(C())也可以输入 Option<B>并通过检查,
    • 好的,lambda 通过了 (Int) -> Option<B> 的检查.


    健全性检查:如果按如下方式替换第一行会怎样?

    val func: (Int) -> Option<B> = { Option(C()) as Option<C> }
    

    那么它就不会被编译,因为 lambda 内的表达式现在输入为 Option<C>它不是 Option<B> 的子类型.

<小时/>
  • opt1.run { Option1(A()) } //return type is Option<A>; why could this compile?

    在此示例中,编译器为 T 选择的类型不是B ,就是A 。由于类型参数 T 的协变性,编译器可以这样做。 .

    • opt1Option1<B>
    • Option1<out T>T 上的协变,允许替换 T具有任何父类(super class)型 B ,

      这是允许的,因为对于任何 Z这样B : Z , opt1也可以视为 Option1<out Z>感谢out修饰符,然后编译器可以根据接收者类型 Option1<Z> 对调用进行类型检查.

    • T 的替代将是 B 最不常见的父类(super class)型等等X这样 lambda 返回 Option1<X> ,

    • lambda 返回 Option1<A> ,
    • 找到 B 的最不常见的父类(super class)型和A ,
    • 鉴于B : A ,最不常见的父类(super class)型是 A
    • 替代T := A .


    健全性检查:如果按如下方式更改表达式会怎样?

    opt1.run { Option1(0) }
    

    它仍然会成功编译,但推断的返回类型将是 Option1<Any> 。根据上面的说法,这是完全合理的,因为 B 是最不常见的父类(super class)型。和IntAny .

<小时/>

免责声明:这不是编译器内部工作的方式,但使用这种推理方式您可能经常会得到与编译器的结果一致的结果。

关于generics - Kotlin 泛型 : counterintuitive type inference and checking with out keyword,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55765128/

相关文章:

java - 在java中使用 'diamond'表示法表示方法

Java,可以修改抽象类层次结构中父类(super class)的方法声明吗?

c# - 为什么泛型方法不能将具体(派生)类转换为其父类

kotlin - 如何使用位在 Kotlin 中存储配置?

c++ - 具有 CRTP 可克隆类的无效协变类型

java - LambdaConversionException 与泛型 : JVM bug?

android - 如何从选定的 mp3 文件中提取信息?

JavaFX 窗口在左上角打开然后跳到中心

c# - 为什么 Resharper 使用此代码说 "Co-variant array conversion from string[] to object[] can cause run-time exception on write operation"?