Templates Kotlin似乎是编译的递归模板类型

Templates Kotlin似乎是编译的递归模板类型,templates,generics,kotlin,Templates,Generics,Kotlin,在工作中进行代码审查时,遇到了我以前从未见过的模板类型的使用。乍一看,代码似乎不应该编译,因为定义似乎是递归的。我将其归结为最简单的可验证示例: interface Bar<T> interface Foo<T: Bar<T>> // Surely this is recursive? 接口栏 接口Foo//这肯定是递归的吗? 我对模板类型如何工作的理解是: 接口Foo-aT的Foo,无约束 接口Foo-一个T的Foo,其中T被约束到一个条 假设我上面说的

在工作中进行代码审查时,遇到了我以前从未见过的模板类型的使用。乍一看,代码似乎不应该编译,因为定义似乎是递归的。我将其归结为最简单的可验证示例:

interface Bar<T>
interface Foo<T: Bar<T>> // Surely this is recursive?
接口栏
接口Foo//这肯定是递归的吗?
我对模板类型如何工作的理解是:

接口Foo
-a
T
Foo
,无约束

接口Foo
-一个
T
Foo
,其中
T
被约束到一个

假设我上面说的是真的,那么这对我来说没有意义:

接口栏
-一个
T
,对
T

接口Foo
-一个
T
Foo
,其中
T
被约束到一个

呃,噢,
T
如何用
Bar
来定义呢

我们知道
T
是一个
Bar
,所以如果我们在
Bar
中替换
T
,那么它就是一个
Bar

对于
Bar
,我们仍然没有解决
t
。。。为了便于讨论,让我们再次替换
T
。现在我们有了
T
作为
Bar
。当然,这是无限的。

(递归有界量化)是一个众所周知的设计习惯用法,通常(除其他外)用于

下面是递归泛型的一个实际示例

假设你有一个函数,它在一组可比较的值上运行

fun <T> findMax(collection: Collection<T>): T?
就这些。对吧?

虽然这会起作用,但您需要对返回值进行强制转换,以便它执行任何有用的操作,因为它返回的是一个
可比的
,而不是
T

现在让我们假设我们尝试:

fun <T : Comparable<T>> findMax(collection: Collection<T>): T?
由于返回类型的协方差,这种方法工作得很好,因为编译代码中的
A
SelfReturner)

模板实例化将导致创建一个新的完全独立的类型,而泛型类型在运行时都会被删除到同一个(非泛型)类中

*这并不完全正确;反射可以使用一些泛型类型信息,这就是为什么它可以工作的原因,但它仅在有限的情况下可用


有趣的事实:维基百科声称这是偶然发现的

Jan Falkin无意中从派生类派生了基类

因此,即使对于提出这个概念的人来说,它似乎也同样令人困惑

是的,没有引用,但我们不要破坏魔法。:)

递归类型界 您所指的模式在JVM世界中称为递归类型绑定。在泛型中,当引用类型具有由引用类型本身限定的类型参数时,则称该类型参数具有递归类型限定

例如,在泛型类型
Fruit
中,
Fruit
是引用类型,其类型参数
T
Fruit
本身的限制,因此,类型参数
T
具有递归类型限制
Fruit

让我们解决一个简单的问题来逐步理解这个概念


问题 假设我们必须按水果的大小来分类。我们被告知,我们只能比较相同类型的水果。例如,我们无法将苹果与橙子进行比较(双关语)

因此,我们创建一个简单的类型层次结构,如下所示

水果.kt

interface Fruit {
    val size: Int
}
class Apple(override val size: Int) : Fruit, Comparable<Apple> {

    override operator fun compareTo(other: Apple): Int {
        return size.compareTo(other.size)
    }
}
class Orange(override val size: Int) : Fruit, Comparable<Orange> {

    override operator fun compareTo(other: Orange): Int {
        return size.compareTo(other.size)
    }
}
fun main() {
    val apple1 = Apple(1)
    val apple2 = Apple(2)
    println(apple1 > apple2)   // No error

    val orange1 = Orange(1)
    val orange2 = Orange(2)
    println(orange1 < orange2) // No error

    println(apple1 < orange1)  // Error: different types
}
interface Fruit : Comparable<Fruit> {
    val size: Int
    override operator fun compareTo(other: Fruit): Int {
        return size.compareTo(other.size)
    }
}
class Apple(override val size: Int) : Fruit
class Orange(override val size: Int) : Fruit
interface Fruit<T> : Comparable<T> {
    val size: Int
    override operator fun compareTo(other: T): Int {
        return size.compareTo(other.size) // Error: size not available.
    }
}
class Apple(override val size: Int) : Fruit<Apple>
class Orange(override val size: Int) : Fruit<Orange>
interface Fruit<T : Fruit<T>> : Comparable<T> {
    val size: Int
    override operator fun compareTo(other: T): Int {
        return size.compareTo(other.size)
    }
}
class Apple(override val size: Int) : Fruit<Apple>
class Orange(override val size: Int) : Fruit<Orange>
Apple.kt

interface Fruit {
    val size: Int
}
class Apple(override val size: Int) : Fruit, Comparable<Apple> {

    override operator fun compareTo(other: Apple): Int {
        return size.compareTo(other.size)
    }
}
class Orange(override val size: Int) : Fruit, Comparable<Orange> {

    override operator fun compareTo(other: Orange): Int {
        return size.compareTo(other.size)
    }
}
fun main() {
    val apple1 = Apple(1)
    val apple2 = Apple(2)
    println(apple1 > apple2)   // No error

    val orange1 = Orange(1)
    val orange2 = Orange(2)
    println(orange1 < orange2) // No error

    println(apple1 < orange1)  // Error: different types
}
interface Fruit : Comparable<Fruit> {
    val size: Int
    override operator fun compareTo(other: Fruit): Int {
        return size.compareTo(other.size)
    }
}
class Apple(override val size: Int) : Fruit
class Orange(override val size: Int) : Fruit
interface Fruit<T> : Comparable<T> {
    val size: Int
    override operator fun compareTo(other: T): Int {
        return size.compareTo(other.size) // Error: size not available.
    }
}
class Apple(override val size: Int) : Fruit<Apple>
class Orange(override val size: Int) : Fruit<Orange>
interface Fruit<T : Fruit<T>> : Comparable<T> {
    val size: Int
    override operator fun compareTo(other: T): Int {
        return size.compareTo(other.size)
    }
}
class Apple(override val size: Int) : Fruit<Apple>
class Orange(override val size: Int) : Fruit<Orange>
Apple.kt

interface Fruit {
    val size: Int
}
class Apple(override val size: Int) : Fruit, Comparable<Apple> {

    override operator fun compareTo(other: Apple): Int {
        return size.compareTo(other.size)
    }
}
class Orange(override val size: Int) : Fruit, Comparable<Orange> {

    override operator fun compareTo(other: Orange): Int {
        return size.compareTo(other.size)
    }
}
fun main() {
    val apple1 = Apple(1)
    val apple2 = Apple(2)
    println(apple1 > apple2)   // No error

    val orange1 = Orange(1)
    val orange2 = Orange(2)
    println(orange1 < orange2) // No error

    println(apple1 < orange1)  // Error: different types
}
interface Fruit : Comparable<Fruit> {
    val size: Int
    override operator fun compareTo(other: Fruit): Int {
        return size.compareTo(other.size)
    }
}
class Apple(override val size: Int) : Fruit
class Orange(override val size: Int) : Fruit
interface Fruit<T> : Comparable<T> {
    val size: Int
    override operator fun compareTo(other: T): Int {
        return size.compareTo(other.size) // Error: size not available.
    }
}
class Apple(override val size: Int) : Fruit<Apple>
class Orange(override val size: Int) : Fruit<Orange>
interface Fruit<T : Fruit<T>> : Comparable<T> {
    val size: Int
    override operator fun compareTo(other: T): Int {
        return size.compareTo(other.size)
    }
}
class Apple(override val size: Int) : Fruit<Apple>
class Orange(override val size: Int) : Fruit<Orange>
橙色.kt

interface Fruit {
    val size: Int
}
class Apple(override val size: Int) : Fruit, Comparable<Apple> {

    override operator fun compareTo(other: Apple): Int {
        return size.compareTo(other.size)
    }
}
class Orange(override val size: Int) : Fruit, Comparable<Orange> {

    override operator fun compareTo(other: Orange): Int {
        return size.compareTo(other.size)
    }
}
fun main() {
    val apple1 = Apple(1)
    val apple2 = Apple(2)
    println(apple1 > apple2)   // No error

    val orange1 = Orange(1)
    val orange2 = Orange(2)
    println(orange1 < orange2) // No error

    println(apple1 < orange1)  // Error: different types
}
interface Fruit : Comparable<Fruit> {
    val size: Int
    override operator fun compareTo(other: Fruit): Int {
        return size.compareTo(other.size)
    }
}
class Apple(override val size: Int) : Fruit
class Orange(override val size: Int) : Fruit
interface Fruit<T> : Comparable<T> {
    val size: Int
    override operator fun compareTo(other: T): Int {
        return size.compareTo(other.size) // Error: size not available.
    }
}
class Apple(override val size: Int) : Fruit<Apple>
class Orange(override val size: Int) : Fruit<Orange>
interface Fruit<T : Fruit<T>> : Comparable<T> {
    val size: Int
    override operator fun compareTo(other: T): Int {
        return size.compareTo(other.size)
    }
}
class Apple(override val size: Int) : Fruit<Apple>
class Orange(override val size: Int) : Fruit<Orange>
解决方案

在这一步中,我们通过将
compareTo()
方法的重复代码移动到接口来消除它。我们的扩展类
Apple
Orange
不再受到普通代码的污染

问题

现在的问题是,我们现在能够比较不同的类型,比较苹果和橙子不再给我们一个错误:

println(apple1 < orange1)    // No error
问题

但是在这一步中,我们的
Fruit
类不会编译。编译器不知道
T
size
属性。这是因为我们的
Fruit
类没有任何绑定。因此,
T
可以是任何类,不可能世界上的每个类都有
size
属性。因此,编译器没有识别
T
size
属性是正确的


引入递归类型界 水果.kt

interface Fruit {
    val size: Int
}
class Apple(override val size: Int) : Fruit, Comparable<Apple> {

    override operator fun compareTo(other: Apple): Int {
        return size.compareTo(other.size)
    }
}
class Orange(override val size: Int) : Fruit, Comparable<Orange> {

    override operator fun compareTo(other: Orange): Int {
        return size.compareTo(other.size)
    }
}
fun main() {
    val apple1 = Apple(1)
    val apple2 = Apple(2)
    println(apple1 > apple2)   // No error

    val orange1 = Orange(1)
    val orange2 = Orange(2)
    println(orange1 < orange2) // No error

    println(apple1 < orange1)  // Error: different types
}
interface Fruit : Comparable<Fruit> {
    val size: Int
    override operator fun compareTo(other: Fruit): Int {
        return size.compareTo(other.size)
    }
}
class Apple(override val size: Int) : Fruit
class Orange(override val size: Int) : Fruit
interface Fruit<T> : Comparable<T> {
    val size: Int
    override operator fun compareTo(other: T): Int {
        return size.compareTo(other.size) // Error: size not available.
    }
}
class Apple(override val size: Int) : Fruit<Apple>
class Orange(override val size: Int) : Fruit<Orange>
interface Fruit<T : Fruit<T>> : Comparable<T> {
    val size: Int
    override operator fun compareTo(other: T): Int {
        return size.compareTo(other.size)
    }
}
class Apple(override val size: Int) : Fruit<Apple>
class Orange(override val size: Int) : Fruit<Orange>
注意,在上面的
Apple
类中,我们错误地传递了
Orange
,而不是
Apple
本身作为类型参数。这导致使用
compareTo(other:T)
方法来采用
Orange
而不是
Apple

现在,我们在比较不同类型时不再出现错误,并且突然无法将苹果与苹果进行比较:

println(apple1 < orange1)    // No error
println(apple1 > apple2)     // Error
println(apple1apple2)//错误
因此,开发人员在扩展类时需要小心


无限递归不可能 声明
Fruit
确保编译器只允许类型
Fruit
的子类型<代码>水果
水果
等不是
水果
的子类型,换句话说,它们不在范围内

例如,如果我们以以下方式使用声明:

class Orange(override val size: Int) : Fruit<Fruit<Orange>>
橙色类(覆盖值大小:Int):水果
编译器将给出错误:
类型参数不在其范围内

对于
水果
,没有可以想象的用例,因此编译器也不允许这样做。只允许第一级,即
水果
水果

这两个