2015-05-18 7 views
15

Ich möchte die generische äußere Klasse mit innerer Klasse als Parameter haben. Ich habe erwartet, dass ich von der Basisklasse abstamme und sie entweder innerlich nutze oder auch innerlich ableite. Auf jeder Ebene habe ich erwartet, die innere Klassennutzung ausgehend vom aktuellen Ableitungsniveau zu begrenzen.Wie rekursive gebundene Generics mit innerer Klasse in Java instanziieren?

Leider treffe ich verschiedene Fehler und Warnungen bei der Verwendung des Musters, so dass ich mir nicht vorstellen kann, wie man es benutzt.

package tests.java; 

public class Try_GenericInnerRecursion { 

    // base class, consisting of outer and inner parts 
    public static class Outer1<E extends Outer1<E>.Inner1> { 
     public class Inner1 { 
     } 

     public void addElement(E e) { 
      System.out.println("Added " + e.toString()); 
     } 
    } 

    // extending outer, but not inner 
    public static class Outer2<E extends Outer1<E>.Inner1> extends Outer1<E>{ 

    } 

    // extending both outer and inner 
    public static class Outer3<E extends Outer3<E>.Inner3> extends Outer1<E>{ 
     public class Inner3 extends Inner1 { 
     } 
    } 

    // extending both outer and inner and stopping extension 
    public static class Outer4 extends Outer1<Outer4.Inner4> { 
     public class Inner4 extends Outer1<Inner4>.Inner1 { 
     } 
    } 



    // instantiating 
    public static void main(String[] args) { 

     Outer1<Outer1.Inner1> a1; // WARNING: Outer1.Inner1 is a raw type 

     a1 = new Outer1<Outer1.Inner1>(); // WARNING: Outer1.Inner1 is a raw type 

     Outer1<?> a2; // OK 

     a2 = new Outer1<?>(); // ERROR: Cannot instantiate the type Outer1<?> 

     Outer1<Outer1<?>.Inner1> a3; // ERROR: Bound mismatch: The type Outer1<?>.Inner1 is not a valid substitute for the bounded parameter <E extends Outer1<E>.Inner1> of the type Outer1<E> 

    Outer1<? extends Outer1<?>.Inner1> a4; // OK 

    a4 = new Outer1<Outer1.Inner1>(); // ERROR: Type mismatch 

      Outer2<Outer1.Inner1> b1; // WARNING: Outer1.Inner1 is a raw type 

      b1 = new Outer2<Outer1.Inner1>(); // WARNING: Outer1.Inner1 is a raw type 

     // and so on 
    } 

} 

Wie kann ich dieses Muster richtig verwenden?

+0

Erweiterung wie 'Outer4' ist der einzige Weg, eine solche Art zu verwenden (da sonst die Parametrierung der Art unendlich rekursiv). Gibt es einen Grund, warum Sie eine so strikte Erklärung brauchen? 'E E erweitert Outer1 .Inner1' wird viel weniger Kopfschmerzen. Wie werden Sie diese Klassen verwenden? – Radiodef

Antwort

3

Ich glaube, Sie einfach

tun können
DerivedCorpus1<?> 

während Wildcard-Capture wird es

DerivedCorpus1<x> where x extends Corpus<x>.Element 

die korrekt begrenzt ist.

In Ihrem exmaple

DerivedCorpus1<? extends Corpus<?>.Element> 

während Wildcard-Capture wird es

DerivedCorpus1<x> where x extends Corpus<x>.Element 
         and x extends Corpus<?>.Element 

offenbar, die extends Corpus<?>.Element Klausel überflüssig ist.

+0

Die Klasse kann immer noch nicht ohne Fehler oder Warnungen instanziiert werden. –

3

Outer1<Outer1.Inner1> a1; // WARNING: Outer1.Inner1 is a raw type

Eigentlich habe ich bekommen "Typargument Outer1.Inner1 ist nicht innerhalb der Grenzen der Typ-Variable e`".

Outer1.Inner1 ist ein Rohtyp, weil Outer1 ein Rohtyp ist. Um einen Nicht-Rohtyp zu verwenden, müssten Sie Outer1<something>.Inner1 schreiben. Das something müsste aber wiederum Outer1<something>.Inner1 verlängern. Um eine Rekursion wie diese zu haben, brauchen Sie einen rekursiven Typ. Da Inner1 eine nicht statische innere Klasse ist, hat sie leider eine implizite Referenz auf eine Instanz von Outer1, und daher müsste jede Klasse, die sie erweitert, auch eine umschließende Instanz von Outer1 haben. Outer4 und Inner4 grundsätzlich tun dies.

Outer4 a1 = new Outer4(); // compiles fine 

a2 = new Outer1<?>(); // ERROR: Cannot instantiate the type Outer1<?>

können Sie tun nie new something<?>().

Outer1<Outer1<?>.Inner1> a3; // ERROR: Bound mismatch: The type Outer1<?>.Inner1 is not a valid substitute for the bounded parameter <E extends Outer1<E>.Inner1> of the type Outer1<E>

Dies gilt. Outer1<?>.Inner1 ist kein Untertyp von Outer1<E>.Inner1 - es ist andersherum - Outer1<E>.Inner1 ist ein Untertyp von Outer1<?>.Inner1. Dies ist genau wie ArrayList<?> ist kein Untertyp von ArrayList<String>; es ist anders herum.

Outer1<? extends Outer1<?>.Inner1> a4; // OK

Das ist in Ordnung, weil Sie einen Platzhalter auf der obersten Ebene haben, und die Ihre Wildcard gebunden schneidet den Typ-Parameter E ‚s gebunden. In der Tat muss alles, was die ursprüngliche Grenze der E erfüllt, diese Grenze erfüllen, so dass diese Grenze nutzlos ist, und dies ist das gleiche wie oben beschrieben Outer1<?> a2;.

a4 = new Outer1<Outer1.Inner1>(); // ERROR: Type mismatch

Geht nicht, für, unter anderem, aus dem gleichen Grund, dass a1 nicht (Outer1.Inner1E ‚s gebunden erfüllt nicht) funktioniert. Es erfüllt auch nicht Ihre Grenze (Outer1<?>.Inner1) glaube ich.

Outer2<Outer1.Inner1> b1; // WARNING: Outer1.Inner1 is a raw type

Dies gibt tatsächlich die gleichen Fehler wie a1, aus dem gleichen Grunde

+0

Zusammenfassend Ihre Antwort Sie meinen, dass "es unmöglich ist, Klasse rekursiv in den Fall in Java zu instanziieren" richtig? –

+0

@SuzanCioc: Nicht richtig, weil zum Beispiel 'Outer4' funktioniert – newacct

+0

Aber' Outer4' ist nicht generisch. Ich möchte nämlich Generika instantiieren. –

1

Generic specification

Bevor ich auf die Instanziierung Probleme gehe ich Ihre allgemeinen revidieren definition:

Outer1<E extends Outer1<E>.Inner1> 

Outer1 sollte im Grunde Elemente des Typs Inner1 oder alle untergeordneten Klassen davon enthalten. Deshalb ist die Art des Outer1 Objekt ist nicht von Bedeutung und kann mit dem folgenden Code ersetzt werden:

Outer1<E extends Outer1<?>.Innter1> 

Auch haben die Verlängerungs Klassen wurden unter Verwendung der oben genannten leicht modifiziert.

Instanziierung

Für die Instanziierung von Objekten angegeben Sie wie folgt vor:

Outer1<Outer1.Inner1> a1;   // WARNING: Outer1.Inner1 is a raw type 
a1 = new Outer1<Outer1.Inner1>(); // WARNING: Outer1.Inner1 is a raw type 

Wie newacct bereits erwähnt wurde, Outer1 wird mit einem generischen Typ deklariert wenn Sie nicht taten Geben Sie bei der Instanziierung des Objekts eine beliebige Zahl an.

Outer1<Outer1<?>.Innter1> a1;   // or simply Outer1<?> a1; 
a1 = new Outer1<Outer1<?>.Innter1>(); // or simply a1 = new Outer1<>(); 

Ähnliche für die Zuordnung von Klassen, die eine Basisklasse auf eine Art von dieser Basisklasse erweitert: Der Code sollte daher auf den folgenden geändert werden.

Sie geben mit der generischen Typdefinition an, welche Art von Elementen die Instanz enthalten soll.

Eine Erklärung wie

Outer1<Outer1<?>.Innter1> test; 

werden alle Instanzen akzeptieren Outer1 erstreckt, die auch den generischen Typ entsprechen.Dies bedeutet, dass Sie nur

test = new Outer1<Outer1<?>.Inner1>(); 
test = new Outer2<Outer2<?>.Inner1>(); 

zuordnen können, aber nicht Outer3 oder sogar Outer4!

Auch wenn Sie den Code weiter modifizieren, um z.

public static class Outer5<E extends Outer1<?>.Inner1> extends Outer1<E>{ 
    public class Inner5 extends Inner1 { 
    } 
} 

Sie sind nur in der Lage

test = new Outer5<Outer5<?>.Inner1>(); 

hinzufügen, aber nicht

test = new Outer5<Outer5<?>.Inner5>(); // FAILS! 

auf etwas, das Inner1 als Typargument definiert.

Es gibt jedoch eine recht einfache Lösung für dieses Problem. Wenn Sie das Objekt wie folgt definieren:

Outer1<? extends Outer1<?>.Inner1> test2; 

sind Sie tatsächlich in der Lage, die folgenden Dinge zu tun:

test2 = new Outer1<Outer1<?>.Inner1>(); 
test2 = new Outer2<Outer2<?>.Inner1>(); 
test2 = new Outer3<Outer3<?>.Inner3>(); 
test2 = new Outer4(); 
test2 = new Outer5<Outer1<?>.Inner1>(); 
test2 = new Outer5<Outer5<?>.Inner1>(); 
test2 = new Outer5<Outer5<?>.Inner5>(); 

wie Sie jetzt explizit den Compiler gesagt, dass entweder die Inner1 Typ selbst oder einer ihrer Erweiterungen ist erlaubt.

-Code

Ich habe Ihren Code aktualisiert und ein paar neue Aufgaben hinzugefügt, um sicherzustellen, dass die tatsächlich vorgenommenen Änderungen produzieren keine Fehler oder Warnungen kompilieren mit Ausnahme der redundant type arguments in new expression aufgrund explizit die Typargumente spezifiziert anstelle der Verwendung des Diamant-Operator <>:

public class GenericInnerRecursion 
{ 
    // base class, consisting of outer and inner parts 
    public static class Outer1<E extends Outer1<?>.Inner1> { 
     public class Inner1 { 
     } 

     public void addElement(E e) { 
      System.out.println("Added " + e.toString()); 
     } 
    } 

    // extending outer, but not inner 
    public static class Outer2<E extends Outer1<?>.Inner1> extends Outer1<E>{ 

    } 

    // extending both outer and inner 
    public static class Outer3<E extends Outer3<?>.Inner3> extends Outer1<E>{ 
     public class Inner3 extends Inner1 { 
     } 
    } 

    // extending both outer and inner and stopping extension 
    public static class Outer4 extends Outer1<Outer4.Inner4> { 
     public class Inner4 extends Outer1<Inner4>.Inner1 { 
     } 
    } 

    public static class Outer5<E extends Outer1<?>.Inner1> extends Outer1<E>{ 
     public class Inner5 extends Inner1 { 
     } 
    } 


    // instantiating 
    public static void main(String[] args) { 

     Outer1<Outer1<?>.Inner1> a1; 
     a1 = new Outer1<Outer1<?>.Inner1>(); 

     Outer1<?> a2; 
     a2 = new Outer1<>(); 

     Outer1<Outer1<?>.Inner1> a3; 

     Outer1<? extends Outer1<?>.Inner1> a4; 
     a4 = new Outer1<Outer1<?>.Inner1>(); 

     Outer2<Outer1<?>.Inner1> b1; 
     b1 = new Outer2<Outer1<?>.Inner1>(); 

     // and so on 

     // assigning extension-classes to the parent-class 
     Outer1<Outer1<?>.Inner1> c1; 
     c1 = new Outer2<Outer2<?>.Inner1>(); 
     // assigning inner-extension-classes to parent-class 
     Outer1<Outer3<?>.Inner3> c2; 
     c2 = new Outer3<Outer3<?>.Inner3>(); 
     // assigning extension class without specified generics to parent class 
     Outer1<Outer4.Inner4> c3; 
     c3 = new Outer4(); 

     Outer1<Outer1<?>.Inner1> test; 
     test = new Outer1<>(); 
     test = new Outer2<>(); 
     test = new Outer5<Outer5<?>.Inner1>(); 

     Outer1<? extends Outer1<?>.Inner1> test2; 
     test2 = new Outer1<Outer1<?>.Inner1>(); 
     test2 = new Outer2<Outer2<?>.Inner1>(); 
     test2 = new Outer3<Outer3<?>.Inner3>(); 
     // new Outer3<Outer3<?>.Inner1>(); not possible as generic type extends Outer3<?>.Inner3 and not Outer1<?>.Inner1! 
     test2 = new Outer4(); 
     test2 = new Outer5<Outer1<?>.Inner1>(); 
     test2 = new Outer5<Outer5<?>.Inner1>(); 
     test2 = new Outer5<Outer5<?>.Inner5>(); 
    } 
} 
Verwandte Themen