简体   繁体   English

如果 Kotlin 中的类型参数受另一个类型参数的限制,为什么它不能有任何其他限制?

[英]Why can't type parameter in Kotlin have any other bounds if it's bounded by another type parameter?

Here is the minimal demo code that shows this problem:这是显示此问题的最小演示代码:

interface A

fun <T1, T2> test() where T2 : T1, T2 : A {}

When I try to compile it, compiler will complain:当我尝试编译它时,编译器会抱怨:

Error:(81, 25) Kotlin: Type parameter cannot have any other bounds if it's bounded by another type parameter错误:(81, 25) Kotlin:如果类型参数受另一个类型参数限制,则类型参数不能有任何其他限制

I read Kotlin Language Specification , but only find the following bound restriction:我阅读了 Kotlin Language Specification ,但只找到以下绑定限制:

A type-parameter cannot specify itself as its own bound, and several type-parameters cannot specify each other as a bound in a cyclic manner.一个type-parameter不能指定自己为自己的bound,多个type-parameter不能循环指定对方为bound。

It doesn't explain the restriction I meet.它没有解释我遇到的限制。

I explore Kotlin's issue tracker, and I find an issue about this restriction: Allow to inherit a type parameter from another type parameter and a class: KT-13768 .我探索了 Kotlin 的问题跟踪器,发现了一个关于此限制的问题: Allow to inherit a type parameter from another type parameter and a class: KT-13768 However, this issue has been rejected by the following reason (update on May 6th, 2017: this issue has been reopened by Stanislav Erokhin):然而,这个问题被拒绝了,原因如下(2017年5月6日更新:这个问题已经被Stanislav Erokhin重新打开):

I don't think we can compile the code correctly to JVM if we remove this restriction.如果我们删除此限制,我认为我们无法将代码正确编译为 JVM。

By Andrey Breslav安德烈布雷斯拉夫

So the question is: why can't we compile the code correctly to JVM if we remove this restriction?那么问题来了:如果去掉这个限制,为什么不能将代码正确编译为JVM呢?

The same demo works in Scala:同样的演示适用于 Scala:

trait A

def test[T1, T2 <: T1 with A](): Unit = {}

It indicates that Scala can compile the code correctly to JVM. Why can't Kotlin?说明Scala可以正确编译出JVM的代码,为什么Kotlin不能呢? Is it a restriction to guarantee a decidable subtyping in Kotlin (I guess. Subtyping is undecidable for Scala (Scala has a Turing-complete type system). Kotlin may want decidable subtyping like C#.)?保证 Kotlin 中的可判定子类型是否有限制(我猜。Scala 的子类型不可判定(Scala 具有图灵完备类型系统)。Kotlin 可能需要像 C# 这样的可判定子类型。)?

Update after answered by @erokhins ( https://stackoverflow.com/a/43807444/7964561 ): @erokhins ( https://stackoverflow.com/a/43807444/7964561 ) 回答后更新:

There are some subtle issues when supporting something forbidden by Java but allowed by JVM, especially in Java interoperability.在支持 Java 禁止但 JVM 允许的内容时存在一些微妙的问题,尤其是在 Java 互操作性方面。 I find an interesting issue when digging into bytecode generated by scalac.在深入研究 scalac 生成的字节码时,我发现了一个有趣的问题。 I modify Scala code in demo as follow:我将demo中的Scala代码修改如下:

trait A

trait B

def test[T1 <: B, T2 <: T1 with A](t1: T1, t2: T2): Unit = {}

class AB extends A with B

Scalac will generate the following signature: Scalac 将生成以下签名:

// signature <T1::LB;T2:TT1;:LA;>(TT1;TT2;)V
// descriptor: (LB;LB;)V
public <T1 extends B, T2 extends T1 & A> void test(T1, T2);

Invoke test with test(new AB, new AB) in Scala will succeed, since Scalas invoke signature (LB;LB;)V ;在 Scala 中使用test(new AB, new AB)调用test将成功,因为 Scalas 调用签名(LB;LB;)V ; but invoke with test(new AB(), new AB());但是调用test(new AB(), new AB()); in Java will fail, since Java invokes signature (LB;Ljava/lang/Object;)V , causing java.lang.NoSuchMethodError in runtime. in Java 将失败,因为 Java 调用签名(LB;Ljava/lang/Object;)V ,在运行时导致java.lang.NoSuchMethodError It means scalac generates something cannot be invoked in Java after relaxing this restriction.这意味着放宽此限制后,scalac 会生成无法在 Java 中调用的内容。 Kotlin may meet the same issue after relaxing it. Kotlin放宽后可能会遇到同样的问题。

This restrictions was made because java(language) has it: 这个限制是因为java(语言)有它:

  interface A {}
  // Error:(7, 26) java: a type variable may not be followed by other bounds
  <T1, T2 extends T1 & A> void test() {} 

And we suppose that this forbidden also in bytecode level. 我们认为这也禁止在字节码级别。 I dig into it and seems like it is allowed, and scalac generate the following signature: 我深入研究它似乎是允许的,并且scalac生成以下签名:

  // access flags 0x1
  // signature <T1:Ljava/lang/Object;T2:TT1;:LA;>()V
  // declaration: void test<T1, T2T1 extends A>()
  public test()V

So, we probably can support such cases it in the future versions of kotlin. 所以,我们可能会在未来版本的kotlin中支持这种情况。

PS As far as I know Kotlin has decidable subtyping, and decidability isn't affected by this. PS据我所知,Kotlin具有可判定的子类型,并且可判定性不受此影响。

If you add the following suppress annotation, it should work:如果添加以下抑制注释,它应该可以工作:

interface A

@Suppress("BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER")
fun <T1, T2> test() where T2 : T1, T2 : A {}

Reference: https://discuss.kotlinlang.org/t/bug-in-where/25011/2参考: https://discuss.kotlinlang.org/t/bug-in-where/25011/2

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM