2015-11-05 14 views
14

Betrachten Sie dieses Beispiel:selbstbezüglicher generische Typen

public final class Main<T extends Main<T>> { 

    public static void main(String[] args) { 
     Main<?> main = new Main<>(); 
    } 
} 

Das perfekt kompiliert. Wenn ich jedoch versuche, diese Kompilierung ohne Verwendung der Raute durchzuführen, kann ich sie nur mit einem Rohtyp ausführen.

Main<?> main = new Main();  

Versuche ohne rohe Typen funktionieren nicht:

Main<?> main = new Main<?>();    // None of 
Main<?> main = new Main<Main<?>>();  // these 
Main<?> main = new Main<Main<Main<?>>>(); // compile 

Warum also die ursprüngliche Version mit dem Diamant Arbeit? Was ist der abgeleitete Typ, wenn Sie Main<?> main = new Main<>(); schreiben?

Folgt es einem rohen Typ, oder leitet es eine Art von unendlich verschachtelten Typen ab?

Main<Main<Main<Main<...>>>> 
+3

Ich denke, Sie sollten "Main" mit "Buffalo" in diesem Beitrag ersetzen. – djechlin

+0

Sie haben also die endlose Rekursion im Java Compiler erfunden. Netter Job, wie es! – gaborsch

+0

'Enum' hat eine ähnliche Deklaration, also was du vorfindest, ist nichts neues. – Makoto

Antwort

3

Die ? in Main<?> ist ein Platzhalter, der jede Art an bind Zeit sein kann.

Jede ?, die in Quelle schreiben kann einen anderen Typ (in Fehlermeldungen als capture#2-of ? bezeichnet) sein, so kann man nicht ein Ausdruck der Main<?> einer Variablen eines exprimierbaren zuordnen.

Der Diamant Operator arbeitet hier, weil seine Typinferenz läuft, nachdem die ? s erfasst werden – Ihre Main<> wird nicht Main<?> aber Main<capture#1 of ?> (vorausgesetzt, die Main<?> Sie weisen es capture#1 zu ist). Der Diamantoperator ist also die einzige Syntax, die ein bestimmtes Capture direkt angeben kann, genau wie var in C# ist die einzige Syntax, die direkt einen anonymen Typ angeben kann. (Beachten Sie, dass die Überladungsauflösung mit Inferenzmethode Typ kann auch auf bestimmte Aufnahmen lösen)


Als für das, was der Code bedeutet, new Main<?>() (für jede Erfassung von ?) ist eine Abkürzung für Main<? extends Object> oder in Ihrem Fall, Main<? extends Main<same ?>> (Der Compiler beschränkt die ? automatisch auf die Einschränkungen des Typs). Dies wird zu einer kovarianten Ansicht von Main<>, wobei der Typparameter nur in Main<?> konvertierbar ist (da er tatsächlich irgendein Typ sein kann, so dass Sie nichts außer der Einschränkung annehmen können).

Normalerweise gibt es keinen Grund, so etwas tatsächlich zu erstellen.

+0

So kann der Compiler einen künstlichen Typ 'capture # 1' erzeugen, der' Main 'erweitert, obwohl es keinen echten Typ wie diesen gibt? –

+1

@PaulBoddington: Das ist kein Typ, sondern eine Einschränkung (genau wie 'List ') – SLaks

1

Es ist möglich, etwas zu machen, ohne den Diamant Operator kompiliert, durch eine generische Helfer-Methode (aber natürlich, das wirft dann die Frage, welche Art Argument für den Aufruf der Hilfsmethode abgeleitet werden):

final class Main<T extends Main<T>> { 

    public static void main(String[] args) { 
     Main<?> main = helper(); 
    } 
    private static <T extends Main<T>> Main<T> helper() { 
     return new Main<T>(); 
    } 
}