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
-aT
的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):水果
编译器将给出错误:类型参数不在其范围内
对于水果
,没有可以想象的用例,因此编译器也不允许这样做。只允许第一级,即水果
,水果
等
这两个