2015-06-11 5 views
5

Angesichts der folgenden zwei Klassendefinitionen:Parametrierung Nun Geformt und Capture-Konvertierung in Java

class C1<T extends C1<T>> {} 

class C2<U> extends C1<C2<U>> {} 

und die folgende Typdeklaration:

C1<C2<?>> a; 

Intuitiv fühlt es sich an den deklarierten Typ a gültig sein sollte, aber Dies ist nicht die Art, wie JDK-8u45 sich verhält. Stattdessen bekommen wir so etwas wie die folgende Ausgabe:

Test.java:3: error: type argument C2<?> is not within bounds of type-variable T 
     C1<C2<?>> a; 
      ^
    where T is a type-variable: 
    T extends C1<T> declared in class C1 
1 error 

(Edit: Ich war hier schon ein dingus zu sein, hat dieser Teil beantwortet: C2<?> nicht C1<C2<?>> erstrecken sich die Frage in Bezug auf die Erklärung von c unten. obwohl es noch eine offene Frage.)

Aber C2<?>hatC1<C2<?>> zu erweitern, die belanglos erscheinen würde, um die gebundene zu befriedigen. Die Untersuchung der JLS liefert keine weitere Beleuchtung soweit ich sehen kann. Es sollte nur so einfach sein, wie die Bindung durch die Subtyprelation zu erfüllen, da kein Platzhalter ist und daher Capture-Konvertierung nur eine Identitätskonvertierung für das Argument ist.

Es gibt Situationen, in denen es etwas weniger klar wird, zum Beispiel, nehmen Sie die folgenden Klassendefinitionen:

class C3<T extends C3<?>> {} 

class C4<Y, Z> extends C3<C4<Z, Y>> {} 

class C5<X extends C3<X>> { 
    void accept(X x); 
} 

Das alles ist in Ordnung, aber dann, wenn wir versuchen, die folgende Erklärung ab:

C5<C6<?, ?>> b; 

Dinge werden fremd. C6<?, ?> ist ein Untertyp von C3<C6<?, ?>>, daher sollte die Deklaration gemäß meiner Interpretation der oben angegebenen Spezifikation bezüglich der Deklaration C1<C2<?>> gültig sein. Das Problem ist, dass offensichtlich nicht alle möglichen Subtyp von C6<?, ?> genügen tatsächlich, dass gebunden, so dass nun zum Beispiel C5.accept() seinen Parametertyp C6<?, ?> löst und so Argumente, die den Begrenzungs auf X verletzen können annehmen, dh jeder, wo die Parametrisierungen Y und Z sind nicht identisch.

Wohin gehe ich hier falsch? Ist mein Verständnis der Subtypbeziehung nicht ausreichend?

(Edit: Der folgende Teil der Frage ist noch unbeantwortet, aber ich habe es auf eine neue Frage here bewegt, da es sich um eine ganz andere Frage ist wirklich ... Sorry für ein Chaos und nicht mit der Website sehr gut haha ​​...)

Abgesehen davon habe ich auch einige Probleme mit Capture-Konvertierung in ähnlichen Situationen. Nehmen Sie die folgende Typdeklaration:

C1<? extends C2<?>> c; 

Im Gegensatz zu der gleichlautende Erklärung a am Start, kompiliert diese Fein in JDK-8u45. Wenn wir jedoch die specification for capture conversion untersuchen, scheint es, dass diese Deklaration zu einem Kompilierungsfehler diesmal führen sollte.

Insbesondere ist die obere Grenze des neuen Typs variable capture CAP#T von glb(Bi, Ui[A1:=S1,...,An:=Sn]) gegeben, wobei in diesem Fall löst Bi zu den Platzhalter gebunden C2<?> und Ui[A1:=S1,...,An:=Sn] zu C1<CAP#T> lösen.

Daraus löst glb(C2<?>, C1<CAP#T>) an der Kreuzung Typ C2<?> & C1<CAP#T>, die ungültig ist, weil C2<?> und C1<CAP#T> beide Klassentypen sind, nicht Typen Schnittstelle, aber keiner von ihnen ist ein Subtyp des anderen.

Diese (scheinbare) Regelverletzung wird in der definition of the intersection type selbst deutlicher gemacht.

Ich bin sicher, es ist kein Fehler und ich mache nur ein paar einfache Fehler irgendwo ... aber wenn hier niemand etwas für mich herausfinden kann, werde ich die Compiler-Dev-Mailingliste oder so etwas ausprobieren.

Danke für jede Hilfe!

+0

Es ist ein faszinierendes Thema. Ich bin nur neugierig, ist das ein theoretisches Problem, oder hast du echte Klassen? Ich kann mir einfach nicht vorstellen, wo man 'Klasse C1 > {}' oder 'Klasse C2 erweitert C1 > {}' verwenden würde. – Kris

+0

Gültige Frage! Ich habe keine spezielle Anwendung dieses speziellen Problems in der realen Welt ... Aber ich * muss * das richtige erwartete Verhalten verstehen, da ich eine Reihe von reflektiven Dienstprogrammen über das Typsystem schreibe und möchte, dass das Verhalten der Spezifikation entspricht. JDK. Momentan funktioniert es meistens, einschließlich der Typinferenz des generischen Methodenaufrufs, aber es gibt einige Randfälle (wie diese), die Probleme verursachen. Wenn Sie einen Blick darauf werfen möchten, ist es hier gehostet: https://github.com/StrangeSkies/uk.co.strangeskies. Noch kein Wiki, aber die Javadocs sind alle für das Paket ".reflection" vorhanden. –

+0

Das hat eigentlich wenig mit Capture-Konvertierung zu tun. Stattdessen sollten Sie in 4.10.2 und 4.5.1 nach Antworten suchen. Sie werden in die Irre geführt, solange Sie dies als mit der Capture-Konvertierung verbunden betrachten. Zum Beispiel, Ihre Begründung dafür, warum 'C1 > 'sollte nicht Kompilieren ist Unsinn. (Ich denke, es ist auch falsch, ich glaube nicht, dass es einen Schnittstellentyp gibt, wenn die Capture-Konvertierung * angewendet wurde, weil [Capture-Konvertierung nicht rekursiv angewendet wird]) (http://stackoverflow.com/a/30385343/2891664) aber ich bin gerade aufgewacht, also bin ich ein bisschen benommen.) – Radiodef

Antwort

3

Während
C2<x> extends C1<C2<x>> für jeden Referenztyp x,
es ist nicht der Fall, dass
C2<?> extends C1<C2<?>>

Ein Platzhalter ?ist kein Typ. Es ist ein Typ Argument. Die Syntax ist jedoch (von Entwurf) sehr täuschend.

Lassen Sie uns eine andere Syntax verwenden - wenn es Platzhalter der 1. Ebene gibt, verwenden Sie {} anstelle von <>, z.

List{?}, Map{String, ? extends Number} 

Die Bedeutung von {?} ist eine Vereinigung Art zu erklären

List{? extends Number} == union of List<Number>, List<Integer>, List<Long>, .... 

Es ist leicht zu sehen, dass List<Integer> ist ein Subtyp von List{? extends Number}; und
List{? extends Number} ist ein Subtyp von List{? extends Object}

Allerdings gibt es keine Möglichkeit, dass Foo{?} ist ein Subtyp eines Foo<x>.


In unserer Syntax wird <> reserviert für Typ ersetzt Vars mit Typen. So schreiben wir
List<String>, C2<Integer>, etc. Es ist leicht, ihre Bedeutung zu verstehen - ersetzen Sie einfach T ‚s mit String im Quellcode von List, bekommen wir eine gute alte Ebene Klasse.

interface List<String> 
     String get(int) 

Dies kann nicht für Wildcard getan werden - es macht keinen Sinn

interface List<?> 
     ? get(int) 

So ist es nicht erlaubt new ArrayList{?}() oder class MyList implements List{?}

Also, wie können wir List{?} benutzen?Welche Methoden können wir nennen?

Wenn der Typ eines Ausdruck ein List{?} ist, wissen wir, dass es ein Objekt ist, und das Objekt muss aus unerfindlichen Typx zu einer Unterklasse von List<x> gehören. Dies ist Wildcard-Capture

obj is a List{?} => obj is a List<x>, where x a subtype of Object. 

Auch wenn die genaue Art der x bei der Kompilierung nicht bekannt ist, können wir immer noch die Substitution tun

interface List<x> 
     x get(int) 

so können wir obj.get(0) Sinne des Anrufs; es gibt x zurück, und x ist ein Subtyp von Object; so können wir den Rückgabewert einer Object zuweisen.

+0

Ach ja, richtig du bist, das war ein bisschen ein Hirn furze von meiner Seite. Vielen Dank! Irgendwelche Gedanken zum 'C1 > c; 'Problem mit Schnitttyp auf der oberen Grenze? Ich denke, das könnte ein wenig schwieriger sein, aber vielleicht nicht ... –

+0

Ich habe den zweiten Teil der Frage in eine neue Frage gestellt, so kann ich nur Ihre Antwort als richtig markieren und loswerden all den Cruft, da die Der zweite Teil ist im Grunde ein völlig eigenständiges Thema. Die neue Frage ist hier: http://stackoverflow.com/questions/30788366/capture-conversion-issue-in-java-wr-conciliation-of-jls-and-actual-jdk-behav –