2017-05-04 41 views
15

Oto minimalne kodu demo, który pokazuje ten problem:Dlaczego parametr wpisu w Kotlin nie ma żadnych innych ograniczeń, jeśli jest ograniczony innym parametrem?

interface A 

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

Gdy próbuję go skompilować, kompilator będzie narzekać:

Error:(81, 25) Kotlin: Type parameter cannot have any other bounds if it's bounded by another type parameter

czytam Kotlin Language Specification, ale tylko znaleźć następujące związanego ograniczenie:

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.

Nie wyjaśnia ograniczenia, które spotykam.

Badam tracker problemów z Kotlin, i znajduję problem dotyczący tego ograniczenia: Allow to inherit a type parameter from another type parameter and a class : KT-13768. Jednak kwestia ta została odrzucona przez następującego powodu (aktualizacja na 6 maja, 2017: kwestia ta została ponownie otwarta przez Stanislav Erokhin):

I don't think we can compile the code correctly to JVM if we remove this restriction.

By Andrey Breslav

Więc pytanie brzmi: dlaczego nie możemy poprawnie skompilować kod do JVM, jeśli usuniemy to ograniczenie?

To samo demo działa w Scala:

trait A 

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

To oznacza, że ​​Scala można skompilować kod poprawnie JVM. Dlaczego nie może Kotlin? Czy jest to ograniczenie gwarantujące rozstrzygające podtypy w Kotlin (Chyba Podtyp jest nierozstrzygalny dla Scali (Scala ma system typu Turing-complete) Kotlin może chcieć decydującego podtypu jak C#.)?

Aktualizacja po odpowiedział @erokhins (https://stackoverflow.com/a/43807444/7964561):

Istnieją pewne subtelne kwestie gdy wspierające coś zakazanego przez Java, ale dozwolony przez JVM, zwłaszcza interoperacyjność Java. Znajduję interesujący problem podczas wkopywania do kodu bajtowego generowanego przez skalak. Zmodyfikować kod Scala w demo następująco:

trait A 

trait B 

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

class AB extends A with B 

Scalac wygeneruje następujący podpis:

// 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 z test(new AB, new AB) w Scala uda, ponieważ Scalas powołać podpis (LB;LB;)V; ale wywołanie z test(new AB(), new AB()); w Javie zakończy się niepowodzeniem, ponieważ Java wywołuje podpis (LB;Ljava/lang/Object;)V, powodując w środowisku wykonawczym java.lang.NoSuchMethodError. Oznacza to, że skalak generuje coś, czego nie można wywołać w Javie po złagodzeniu tego ograniczenia. Kotlin może spotkać ten sam problem po złagodzeniu go.

Odpowiedz

7

Te ograniczenia powstał ponieważ java (język) ma go:

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

I przypuszczać, że to zakazane także w poziomie kodu bajtowego. wbijam do niego i wydaje się, że jest to dozwolone, a scalac wygenerować następujący podpis:

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

Więc prawdopodobnie może wspierać takich przypadkach w przyszłych wersjach Kotlin.

P.S.O ile mi wiadomo, Kotlin ma decydujące podtypy, a rozstrzygalność nie ma na to wpływu.

Powiązane problemy