templates - Kotlin 看似递归的可编译模板类型
问题描述
在工作中进行代码审查并遇到了我以前从未见过的模板类型的使用。乍一看,代码似乎不应该编译,因为定义似乎是递归的。我将其归结为最简单的可验证示例:
interface Bar<T>
interface Foo<T: Bar<T>> // Surely this is recursive?
我对模板类型如何工作的理解是:
interface Foo<T>
- 一个Foo
,T
无约束
interface Foo<T : Bar>
- a Foo
of T
, whereT
被限制为 aBar
假设我上面说的是真的,那么这对我来说没有意义:
interface Bar<T>
- a Bar
of T
, 无约束T
interface Foo<T: Bar<T>>
- a Foo
of T
, whereT
被限制为 aBar<T>
呃哦,怎么能用?T
来定义Bar<T>
。
我们知道T
是 a Bar<T>
,所以如果我们替换T
in Bar<T>
,那么它就是 a Bar<Bar<T>>
。
我们还没有解决......T
为了Bar
争论,让我们T
再次替换。现在我们已经T
成为一个Bar<Bar<Bar<T>>>
. 这肯定会进入无穷大,不是吗?
解决方案
CRTP(递归有界量化)是一种众所周知的设计习惯用法,通常(除其他外)用于为通用代码提供某种“自我”类型。
这是递归泛型的一个实际示例。
假设您有一个对一组可比较值进行操作的函数。
fun <T> findMax(collection: Collection<T>): T?
理想情况下,我们希望将此函数限制为仅对Comparable
值集合进行操作:
fun <T> findMax(collection: Collection<Comparable<T>>): Comparable<T>?
就这样。正确的?
虽然这可行,但您需要对返回值进行强制转换才能执行任何有用的操作,因为它返回的是 aComparable<T>
而不是 a T
。
现在假设我们尝试:
fun <T : Comparable<T>> findMax(collection: Collection<T>): T?
好多了。这可确保:
T
是Comparable
- 更重要的
T
是,与自己相媲美
这同样适用于类和继承。
interface SelfReturner<T : SelfReturner<T>> {
fun getSelf(): T
}
class A : SelfReturner<A> {
override fun getSelf(): A // <--
}
由于返回类型协方差,这工作得很好,因为A
是SelfReturner<A>
.
这通常用于允许一个类“知道”它自己的类型,但重要的是要记住它不是万无一失的:
class Impostor : SelfReturner<A> {
override fun getSelf(): A // <-- oops!
}
虽然您对这些泛型的明显递归性是正确的,因为确实可以改为编写
fun <T : Comparable<Comparable<Comparable<...>>>> findMax(collection: Collection<T>): T?
这不会永远持续下去,因为在单级递归之后通常会满足条件(例如String
,我们使用 . 它是a Comparable<String>
,这就是编译器需要检查的全部内容。)
请注意,与例如 C++ 不同,Kotlin 不使用模板。泛型类型信息仅由编译器用于确保代码正确性,并且不会在编译后的代码中保留*(请参阅类型擦除)。
而模板实例化将导致创建一个新的和完全独立的类型,泛型类型在运行时都被删除到同一个(非泛型)类。
* 这并不完全正确;一些通用类型信息可用于反射,这就是类型标记起作用的原因,但它仅在有限的情况下可用。
有趣的事实:维基百科声称这是偶然发现的,
作者 Jan Falkin,他不小心从派生类派生了一个基类
因此,即使对于提出这个概念的人来说,它似乎也同样令人困惑。
是的,没有引用,但我们不要破坏魔法。:)
推荐阅读
- python - less.exe 输出不正确的字符
- pointers - 0xC0000005:析构函数中的访问冲突读取位置错误
- c++ - 为什么 OpenMP 在这个 fft 代码中会卡在 10% 的 CPU 消耗?
- python - Activate code when loop reaches a certain number
- xsd - 针对多个 XSD 的条件 XML 验证 - 忽略某些 XSD 的验证错误,并且仅对某些 XSD 的验证失败
- javascript - 从数组中获取学生的姓名元素具有最高点值并在 jquery 中突出显示该名称
- tcl - 如何获得我想要的输出?
- c# - 有没有办法让建议的操作成为 Botframework v4 上的编号列表?
- julia - 如何通过第 3 个维度合并两个 4 维度数组
- python-3.x - 如何在用户输入之前多线程等待用户输入并做其他事情