如何解决Kotlin中违反有限束限制的问题?

时间:2017-10-11 07:37:33

标签: generics kotlin

假设我在Java中有这个声明,没关系。

abstract class Start<T extends End> {
    public T end;
}

abstract class End<T extends Start> {
    public T start;
}

然而,在Kotlin中它并不合适,因为Kotlin对“循环”类型参数有限制。

abstract class Start<T : End<*>> {
    lateinit var end: T
}

abstract class End<T : Start<*>> {
    lateinit var start: T
}

有什么方法可以在Kotlin中解决这个问题,这样我可以拥有相互依赖的泛型类型吗?

2 个答案:

答案 0 :(得分:8)

不可能只使用一个类型参数。引入Self类型是必要的,这种类型在其他一些语言中是本机支持的。但是,在kotlin中,您必须自己引入Self类型,因为JetBrains officially turned down the request of adding self type

abstract class Start<Self: Start<Self, T>, T: End<T, Self>> {
    lateinit var end: T
}

abstract class End<Self: End<Self, T>, T: Start<T, Self>> {
    lateinit var start: T
}

PS:这个Self可能会导致繁琐的长型。谨慎行事。

答案 1 :(得分:2)

设G是一个有向图,其顶点是程序中所有泛型类型声明的所有类型参数。对于每个通用类型B&lt; ...&gt;中的每个投影类型 - 参数A.在G中每个类型参数T的声明上限集合的B闭包中的每种类型的组成类型的集合中,添加从T到U的边缘,其中U是B的声明的类型参数&lt; ...&GT;对应于类型参数A.如果图G具有循环,则为编译时错误。

注意: 图G中边X→Y的直观含义是“类型参数X的边界的确切含义取决于类型参数Y的边界”。

示例:

以下声明无效,因为有一个边T→T,形成一个循环:

interface A<T : A<*>>

绑定A<*>是具有隐式绑定的投影。如果明确表示该绑定,则类型A<*>采用等效形式A<out A<*>>。以同样的方式,它可以用A<out A<out A<*>>>的等价物进一步重写,依此类推。在其完全展开的形式中,这种界限将是无限的。此规则的目的是避免这种无限类型,并键入与它们相关的检查困难。

以下一对声明无效,因为有T→S和S→T的边,形成一个循环:

interface B<T : C<*>>
interface C<S : B<*>>

以下声明无效,因为边缘K→V和V→K,形成一个循环:

interface D<K: D<K, *>, V: D<*, V>>

另一方面,以下每个声明都有效:

interface A<T : A<T>>
interface D<K, V : D<*, V>>

TODO:这些算法与灵活类型的相互作用。 TODO:在Java中声明的违反这些规则的导入类型。

子类型关系应以归纳方式决定,即必须有一个有限的证明。

interface N<in T>
interface A<S> : N<N<A<A<S>>>>

Official Link