{"id":31,"date":"2014-05-26T12:06:23","date_gmt":"2014-05-26T12:06:23","guid":{"rendered":"http:\/\/vkrastev1blog.com\/?p=31"},"modified":"2014-05-26T15:36:27","modified_gmt":"2014-05-26T15:36:27","slug":"java-generics-explained-in-short-form-with-examples","status":"publish","type":"post","link":"https:\/\/vkrastev1blog.com\/2014\/05\/26\/java-generics-explained-in-short-form-with-examples\/","title":{"rendered":"Java-Generika in Kurzform mit Beispielen erkl\u00e4rt"},"content":{"rendered":"<p>Weitere Informationen finden Sie in diesen FAQ:<\/p>\n<p><a href=\"http:\/\/www.angelikalanger.com\/GenericsFAQ\/JavaGenericsFAQ.html#SECTION1\" rel=\"nofollow\">http:\/\/www.angelikalanger.com\/GenericsFAQ\/JavaGenericsFAQ.html#SECTION1<\/a><\/p>\n<p>Hier in Kurzform einige wichtige Punkte aus dieser FAQ.<\/p>\n<p>Beispiel (eines generischen Typs):<\/p>\n<blockquote><p><tt> Klassenpaar&lt;X,Y&gt; {\u00a0<\/tt><br \/>\n<tt> \u00a0 privates X zuerst;<\/tt><br \/>\n<tt> \u00a0 privat Y Sekunde;<\/tt><tt> \u00a0 \u00f6ffentliches Paar(X a1, Y a2) {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 zuerst = a1;<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 Sekunde = a2;<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 public X getFirst() { return first; }<\/tt><br \/>\n<tt> \u00a0 public Y getSecond() { return second; }<\/tt><br \/>\n<tt> \u00a0 public void setFirst(X arg)\u00a0 { first = arg; }<\/tt><br \/>\n<tt> \u00a0 public void setSecond(Y arg) { second = arg; }<\/tt><br \/>\n<tt> }<\/tt><\/p><\/blockquote>\n<p>Die Klasse <tt> Paar<\/tt> hat zwei Typparameter <tt> X <\/tt> Und <tt> Y<\/tt> .\u00a0 Beim generischen Typ werden sie durch Typargumente ersetzt <tt> Paar<\/tt> wird instanziiert. Zum Beispiel in der Erkl\u00e4rung <tt> Paar&lt;String, Datum&gt;<\/tt> der Typparameter <tt> X<\/tt> wird durch das Typargument ersetzt <tt> Zeichenfolge<\/tt> Und <tt> Y<\/tt> wird ersetzt durch\u00a0 <tt> Datum<\/tt> .<br \/>\nDer Umfang der Bezeichner <tt> X <\/tt> Und <tt> Y<\/tt> Ist<b> <i> <\/i> <\/b> die gesamte Definition der Klasse.\u00a0 In diesem Bereich die beiden Typparameter <tt> X <\/tt> Und <tt> Y<\/tt> werden wie Typen verwendet (mit einigen Einschr\u00e4nkungen). Im obigen Beispiel werden die Typparameter als Argument und R\u00fcckgabetyp von Instanzmethoden und als Typen von Instanzfeldern verwendet.<br \/>\nTypparameter k\u00f6nnen mit Grenzen deklariert werden. Grenzen erm\u00f6glichen den Zugriff auf Methoden des unbekannten Typs, f\u00fcr den der Typparameter steht. In unserem Beispiel rufen wir keine Methoden unbekannter Typen auf <tt> X <\/tt> Und <tt> Y<\/tt> . Aus diesem Grund sind die beiden Typparameter unbegrenzt.<\/p>\n<p>Beispiel (eines konkreten parametrisierten Typs):<\/p>\n<blockquote><p><tt> public void printPair(<b> <span style=\"color:#000099;\"> Paar&lt;String,Long&gt;<\/span> <\/b> Paar) {<\/tt><br \/>\n<tt> \u00a0 System.out.println(\"(\"+pair.getFirst()+\",\"+pair.getSecond()+\")\");<\/tt><br \/>\n<tt> }<\/tt><tt> <b> <span style=\"color:#000099;\"> Paar&lt;String,Long&gt;<\/span> <\/b> limit = neu <b> <span style=\"color:#000099;\"> Paar&lt;String,Long&gt;<\/span> <\/b> (\u201emaximal\u201c,1024L);<\/tt><br \/>\n<tt> printPair(limit);<\/tt><\/p><\/blockquote>\n<p>Beispiel (f\u00fcr einen mit Platzhaltern parametrisierten Typ):<\/p>\n<blockquote>\n<blockquote><p><tt> public void printPair(<b> <span style=\"color:#000099;\"> Paar&lt;?,?&gt;<\/span> <\/b> Paar) {<\/tt><br \/>\n<tt> \u00a0 System.out.println(\"(\"+pair.getFirst()+\",\"+pair.getSecond()+\")\");<\/tt><br \/>\n<tt> }<\/tt><tt> <b> <span style=\"color:#000099;\"> Paar&lt;?,?&gt;<\/span> <\/b> limit = neu <b> <span style=\"color:#000099;\"> Paar&lt;String,Long&gt;<\/span> <\/b> (\u201emaximal\u201c,1024L);<\/tt><br \/>\n<tt> printPair(limit);<\/tt><\/p><\/blockquote>\n<\/blockquote>\n<p>Alle Instanziierungen eines generischen Typs verwenden dieselbe Laufzeittypdarstellung, n\u00e4mlich die Darstellung des Rohtyps. Zum Beispiel die Instanziierungen eines generischen Typs <tt> Liste<\/tt> , wie zum Beispiel <tt> List&lt;Date&gt;<\/tt> , <tt> List&lt;String&gt;<\/tt> , <tt> List&lt;Long&gt;<\/tt> usw. haben zur Kompilierungszeit unterschiedliche statische Typen, aber denselben dynamischen Typ <tt> Liste <\/tt> zur Laufzeit.<\/p>\n<p>Eine Besetzung besteht aus zwei Teilen:<\/p>\n<ul>\n<li>eine vom Compiler zur Kompilierungszeit durchgef\u00fchrte statische Typpr\u00fcfung und<\/li>\n<li>eine dynamische Typpr\u00fcfung, die von der virtuellen Maschine zur Laufzeit durchgef\u00fchrt wird.<\/li>\n<\/ul>\n<p>Der statische Teil sortiert unsinnige Casts aus, die nicht erfolgreich sein k\u00f6nnen, wie zum Beispiel der Cast from <tt> Zeichenfolge<\/tt> Das <tt> Datum<\/tt> oder von <tt> List&lt;String&gt;<\/tt> Das <tt> List&lt;Date&gt;<\/tt> .<\/p>\n<p>Der dynamische Teil verwendet die Laufzeittypinformationen und f\u00fchrt zur Laufzeit eine Typpr\u00fcfung durch.\u00a0 Es erh\u00f6ht a <tt> ClassCastException<\/tt> wenn der dynamische Typ des Objekts nicht der Zieltyp (oder ein Untertyp des Zieltyps) der Umwandlung ist. Beispiele f\u00fcr Besetzungen mit dynamischem Anteil sind die Besetzung von <tt> Objekt<\/tt> Das <tt> Zeichenfolge<\/tt> oder von <tt> Objekt<\/tt> Das <tt> List&lt;String&gt;<\/tt> .\u00a0 Dies sind die sogenannten Downcasts, von einem Supertyp bis hin zu einem Subtyp.<\/p>\n<p>Beispiele f\u00fcr konkrete parametrisierte Typen sind <tt> List&lt;String&gt;<\/tt> , <tt> Map&lt;String,Date&gt;<\/tt> , aber nicht <tt> List&lt;? erweitert Number&gt;<\/tt> oder <tt> Map&lt;String,?&gt;<\/tt> .<\/p>\n<p>Manchmal wird erwartet, dass a <tt> List&lt;Object&gt;<\/tt> w\u00e4re ein Supertyp von a <tt> List&lt;String&gt;<\/tt> , Weil <tt> Objekt<\/tt> ist ein Supertyp von <tt> Zeichenfolge<\/tt> .\u00a0 Diese Erwartung ergibt sich aus der Tatsache, dass f\u00fcr Arrays eine solche Art von Beziehung existiert:\u00a0 <tt> Objekt[]<\/tt> ist ein Supertyp von <tt> String[]<\/tt> , Weil <tt> Objekt<\/tt> ist ein Supertyp von <tt> Zeichenfolge<\/tt> . (Diese Art von Beziehung ist bekannt als <i> Kovarianz<\/i> .) Die Super-Subtyp-Beziehung der Komponententypen erstreckt sich bis in die entsprechenden Array-Typen. F\u00fcr Instanziierungen generischer Typen existiert keine solche Typbeziehung. (Parametrierte Typen sind <i> nicht<\/i> kovariante.)<\/p>\n<p>Das Fehlen einer Super-Subtyp-Beziehung zwischen Instanziierungen desselben generischen Typs hat verschiedene Konsequenzen.\u00a0 Hier ist ein Beispiel.<\/p>\n<p>Beispiel:<\/p>\n<p><tt> void printAll(ArrayList&lt;Object&gt; c) {<\/tt><br \/>\n<tt> \u00a0 for (Objekt o : c)\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 System.out.println(o);<\/tt><br \/>\n<tt> }<\/tt><tt> ArrayList&lt;String&gt; list = new ArrayList&lt;String&gt;();<\/tt><br \/>\n<tt> ... Liste ausf\u00fcllen ...<\/tt><br \/>\n<tt> printAll(list);\u00a0\u00a0 \/\/ Fehler<\/tt><\/p>\n<p>A <tt> ArrayList&lt;String&gt;<\/tt> Das Objekt kann nicht als Argument an eine Methode \u00fcbergeben werden, die nach einem fragt <tt> ArrayList&lt;Object&gt;<\/tt> weil die beiden Typen Instanziierungen desselben generischen Typs sind, aber f\u00fcr <i> anders<\/i> Typargumente und aus diesem Grund sind sie nicht miteinander kompatibel.<\/p>\n<p>Konkrete parametrisierte Typen sind konkrete Instanziierungen eines generischen Typs.\u00a0 Sie sind fast wie Typen; Es gibt nur wenige Einschr\u00e4nkungen.\u00a0 Sie d\u00fcrfen NICHT f\u00fcr folgende Zwecke verwendet werden:<\/p>\n<ul>\n<li>zur Erstellung von Arrays<\/li>\n<li>in der Ausnahmebehandlung<\/li>\n<li>in einem Klassenliteral<\/li>\n<li>in einem <tt> Instanz von<\/tt> Ausdruck<\/li>\n<\/ul>\n<p>Arrays, die Elemente enthalten, deren Typ ein konkreter parametrisierter Typ ist, sind unzul\u00e4ssig.<\/p>\n<p>Beispiel (f\u00fcr einen unzul\u00e4ssigen Array-Typ):<\/p>\n<p><tt> static void test() {<\/tt><br \/>\n<tt> \u00a0 <b> <span style=\"color:#660000;\"> Paar&lt;Integer,Integer&gt;[]<\/span> <\/b> intPairArr = neu <b> <span style=\"color:#660000;\"> Paar&lt;Integer,Integer&gt;[10]<\/span> <\/b> ; <b> <span style=\"color:#660000;\"> \/\/ Fehler<\/span> <\/b> <\/tt><br \/>\n<tt> \u00a0 addElements(intPairArr);\u00a0<\/tt><br \/>\n<tt> \u00a0 Pair&lt;Integer,Integer&gt; pair = intPairArr[1];<\/tt><br \/>\n<tt> \u00a0 Ganzzahl i = pair.getFirst();<\/tt><br \/>\n<tt> \u00a0 pair.setSecond(i);<\/tt><br \/>\n<tt> }<\/tt><br \/>\n<tt> static void addElements(<b> Objekt[]<\/b> objArr) {<\/tt><br \/>\n<tt> \u00a0 objArr[0] = neues Pair&lt;Integer,Integer&gt;(0,0);<\/tt><br \/>\n<tt> \u00a0 objArr[1] = neues Pair&lt;String,String&gt;(\"\",\"\");\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ sollte mit ArrayStoreException fehlschlagen<\/tt><br \/>\n<tt> }<\/tt><\/p>\n<p>Der Compiler verbietet die Erstellung von Arrays, deren Komponententyp ein konkreter parametrisierter Typ ist, z <tt> Paar&lt;Integer,Integer&gt;<\/tt> in unserem Beispiel.\u00a0 Wir haben im vorherigen Eintrag besprochen, warum es sinnvoll ist, dass der Compiler a qualifiziert<span style=\"color:#000000;\"> <tt> Paar&lt;Integer,Integer&gt;[]<\/tt> <\/span> als illegal.\u00a0 Das Hauptproblem besteht darin, dass der Compiler und das Laufzeitsystem sicherstellen m\u00fcssen, dass ein Array ein ist <i> homogen<\/i> Folge von Elementen des gleichen Typs.\u00a0 Eine der Typpr\u00fcfungen, n\u00e4mlich die von der virtuellen Maschine zur Laufzeit durchgef\u00fchrte Array-Store-Pr\u00fcfung, erkennt das fehlerhafte Einf\u00fcgen eines fremden Elements nicht. Im Beispiel die zweite Einf\u00fcgung im <tt> addElements<\/tt> Die Methode sollte fehlschlagen, da ein Zeichenfolgenpaar zu einem Array ganzzahliger Werte hinzugef\u00fcgt wurde, aber sie schl\u00e4gt nicht wie erwartet fehl. Die Gr\u00fcnde wurden im vorherigen Eintrag besprochen.<\/p>\n<p>Wenn wir keine Arrays verwenden k\u00f6nnen, die Elemente enthalten, deren Typ ein konkreter parametrisierter Typ ist, was k\u00f6nnen wir dann als Workaround verwenden?<\/p>\n<p>Betrachten wir drei denkbare Workarounds:<\/p>\n<ul>\n<li>Array von Rohtypen<\/li>\n<li>Array eines unbegrenzten, mit Platzhaltern parametrisierten Typs<\/li>\n<li>Sammlung statt Array<\/li>\n<\/ul>\n<p>W\u00e4hrend Arrays konkreter parametrisierter Typen unzul\u00e4ssig sind, sind Sammlungen konkreter parametrisierter Typen zul\u00e4ssig.<\/p>\n<p>Ein parametrisierter Platzhaltertyp ist eine Instanziierung eines generischen Typs, bei dem mindestens ein Typargument ein Platzhalter ist.\u00a0 Beispiele f\u00fcr mit Platzhaltern parametrisierte Typen sind: <tt> Sammlung&lt;?&gt;<\/tt> , <tt> List&lt;? erweitert Number&gt;<\/tt> , <tt> Komparator&lt;? super String&gt;<\/tt> Und <tt> Paar&lt;String,?&gt;<\/tt> . Ein mit Platzhalterzeichen parametrisierter Typ bezeichnet eine Typfamilie, die konkrete Instanziierungen eines generischen Typs umfasst.\u00a0 Die Art des verwendeten Platzhalters bestimmt, welche konkreten parametrisierten Typen zur Familie geh\u00f6ren.\u00a0 Zum Beispiel der mit Platzhaltern parametrisierte Typ <tt> Sammlung&lt;?&gt;<\/tt> bezeichnet die Familie aller Instanziierungen der <tt> Sammlung<\/tt> Schnittstelle unabh\u00e4ngig vom Typargument.\u00a0 Der mit Platzhaltern parametrisierte Typ <tt> List&lt;? erweitert Number&gt;<\/tt> bezeichnet die Familie aller Listentypen, bei denen der Elementtyp ein Untertyp von ist\u00a0 <tt> Nummer<\/tt> .\u00a0 Der mit Platzhaltern parametrisierte Typ <tt> Komparator&lt;? super String&gt;<\/tt> ist die Familie aller Instanziierungen der <tt> Komparator<\/tt> Schnittstelle f\u00fcr Typargumenttypen, die Supertypen von sind <tt> Zeichenfolge.<\/tt><\/p>\n<p><strong><em>Ein mit Platzhaltern parametrisierter Typ ist kein Typ im regul\u00e4ren Sinne (anders als eine nicht parametrisierte Klasse\/Schnittstelle oder ein Rohtyp).<\/em> <\/strong><\/p>\n<p>F\u00fcr die Typisierung k\u00f6nnen parametrisierte Wildcard-Typen verwendet werden (wie nicht parametrisierte Klassen und Schnittstellen):<\/p>\n<ul>\n<li>als Argument- und R\u00fcckgabetypen von Methoden<\/li>\n<li>als Typ eines Feldes oder einer lokalen Referenzvariablen<\/li>\n<li>als Komponententyp eines Arrays, als Typargument anderer parametrisierter Typen<\/li>\n<li>als Zieltyp in Casts<\/li>\n<\/ul>\n<p>Nicht nur Typen k\u00f6nnen generisch sein, sondern auch Methoden. Statische und nicht statische Methoden sowie Konstruktoren k\u00f6nnen Typparameter haben. Die Syntax zur Deklaration der formalen Typparameter \u00e4hnelt der Syntax f\u00fcr generische Typen. Der Typparameterabschnitt wird durch spitze Klammern begrenzt und erscheint vor dem R\u00fcckgabetyp der Methode. Seine Syntax und Bedeutung ist identisch mit der Typparameterliste eines generischen Typs.<\/p>\n<p>Hier ist das Beispiel eines Generikums <tt> max<\/tt> Methode, die den gr\u00f6\u00dften Wert in einer Sammlung von Elementen eines unbekannten Typs berechnet <tt> A<\/tt> .<\/p>\n<p>Beispiel (einer generischen Methode):<\/p>\n<blockquote><p><tt> Klassensammlungen {<\/tt><br \/>\n<tt> \u00a0 \u00f6ffentliche statische Aufladung <b> <span style=\"color:#000099;\"> &lt;A erweitert Comparable&lt;A&gt;&gt; A<\/span> <\/b> max(Collection&lt;<b> <span style=\"color:#000099;\"> A<\/span> <\/b> &gt; xs) {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 Iterator&lt;<b> <span style=\"color:#000099;\"> A<\/span> <\/b> &gt; xi = xs.iterator();<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 <b> <span style=\"color:#000099;\"> A<\/span> <\/b> w = xi.next();<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 while (xi.hasNext()) {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0 <b> <span style=\"color:#000099;\"> A<\/span> <\/b> x = xi.next();<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0 if (w.compareTo(x) &lt; 0) w = x;<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 }<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 return w;<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> }\u00a0<\/tt><\/p><\/blockquote>\n<p>Der <tt> max<\/tt> Die Methode verf\u00fcgt \u00fcber einen Typparameter mit dem Namen<tt> A<\/tt> .\u00a0 Es handelt sich um einen Platzhalter f\u00fcr den Elementtyp der Sammlung, mit der die Methode arbeitet. Der Typparameter hat eine Grenze; Es muss ein Typ sein, der ein Untertyp von ist <tt> Vergleichbar&lt;A&gt;<\/tt><\/p>\n<p>Beispiel (diesmal vom gleichen generischen Typ). <i> mit<\/i> Grenzen):<\/p>\n<blockquote><p><tt> \u00f6ffentliche Schnittstelle Comparable&lt;T&gt; {<\/tt><br \/>\n<tt> \u00a0 public int CompareTo(T arg);<\/tt><br \/>\n<tt> }<\/tt><br \/>\n<tt> \u00f6ffentliche Klasse TreeMap&lt;Key <b> <span style=\"color:#000066;\"> erweitert Comparable&lt;Key&gt;<\/span> <\/b> ,Data&gt;{<\/tt><br \/>\n<tt> \u00a0 private statische Klasse Entry&lt;K,V&gt; {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0 K-Taste;<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0 V-Wert;<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0 Eintrag&lt;K,V&gt; left = null;<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0 Eintrag&lt;K,V&gt; right = null;<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0 Eintrag&lt;K,V&gt; \u00fcbergeordnetes Element;<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 privater transienter Eintrag&lt;Key,Data&gt; root = null;<\/tt><br \/>\n<tt> \u00a0 ...<\/tt><br \/>\n<tt> \u00a0 private Entry&lt;Key,Data&gt; getEntry(Key key) {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0 Entry&lt;Key,Data&gt; p = root;<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0 Schl\u00fcssel k = Schl\u00fcssel;<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0 while (p != null) {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 int cmp = k.<b> <span style=\"color:#990000;\"> CompareTo(p.key)<\/span> <\/b> ;\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (cmp == 0)<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 R\u00fcckkehr p;<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 sonst wenn (cmp &lt; 0)<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 p = p.links;<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 anders\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 p = p.rechts;\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0 }<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0 null zur\u00fcckgeben;<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 \u00f6ffentlicher boolescher Wert enth\u00e4ltSchl\u00fcssel(Schl\u00fcsselschl\u00fcssel) {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0 return getEntry(key) != null;<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 ...<\/tt><br \/>\n<tt> }<\/tt><\/p><\/blockquote>\n<p>Im obigen Beispiel der Typparameter <tt> Schl\u00fcssel<\/tt> hat die Grenze <tt> Vergleichbar&lt;Key&gt;<\/tt> .\u00a0 Die Angabe einer Grenze hat zwei Auswirkungen:<\/p>\n<ul>\n<li><i> Es erm\u00f6glicht den Zugriff auf die Methoden, die die Grenze angibt<\/i> .\u00a0 Im Beispiel die Grenze <tt> Vergleichbar&lt;Key&gt; <\/tt> erm\u00f6glicht den Zugriff auf die <tt> vergleichenMit<\/tt> Methode, die wir bei der Implementierung unserer aufrufen m\u00f6chten <tt> TreeMap<\/tt> Klasse.<\/li>\n<\/ul>\n<ul>\n<li><i> F\u00fcr die Instanziierung des generischen Typs k\u00f6nnen nur Typen \u201einnerhalb der Grenzen\u201c verwendet werden.<\/i> \u00a0 Im Beispiel ist ein parametrisierter Typ wie z <tt> TreeMap&lt;Number,String&gt;<\/tt> w\u00fcrde abgelehnt werden, weil der Typ <tt> Nummer<\/tt> ist kein Subtyp von <tt> Vergleichbar&lt;Number&gt;<\/tt> .\u00a0 Ein parametrisierter Typ wie <tt> TreeMap&lt;String,String&gt;<\/tt> w\u00fcrde akzeptiert, da der Typ String innerhalb der Grenzen liegt, d. h. ein Subtyp von ist <tt> Vergleichbar&lt;String&gt;<\/tt> .<\/li>\n<\/ul>\n<p>Alle Klassen, Schnittstellen und Aufz\u00e4hlungstypen k\u00f6nnen als Typparameterbindung verwendet werden, einschlie\u00dflich verschachtelter und innerer Typen.\u00a0 Weder primitive Typen noch Array-Typen k\u00f6nnen als Typparameterbindung verwendet werden.<\/p>\n<p>Beispiele (f\u00fcr Typparametergrenzen):<\/p>\n<blockquote><p><tt> Klasse X0 &lt;T erweitert <b> <span style=\"color:#000099;\"> int<\/span> <\/b> &gt; { ... }\u00a0\u00a0\u00a0\u00a0\u00a0 <b> <span style=\"color:#660000;\"> \/\/ Fehler<\/span> <\/b> <\/tt><br \/>\n<tt> Klasse X1 &lt;T erweitert <b> <span style=\"color:#000099;\"> Objekt[]<\/span> <\/b> &gt; { ... } <b> <span style=\"color:#660000;\"> \/\/ Fehler<\/span> <\/b> <\/tt><br \/>\n<tt> Klasse X2 &lt;T erweitert <b> <span style=\"color:#000099;\"> Nummer<\/span> <\/b> &gt; { ... }<\/tt><br \/>\n<tt> Klasse X3 &lt;T erweitert <b> <span style=\"color:#000099;\"> Zeichenfolge<\/span> <\/b> &gt; { ... }<\/tt><br \/>\n<tt> Klasse X4 &lt;T erweitert <b> <span style=\"color:#000099;\"> Lauff\u00e4hig<\/span> <\/b> &gt; { ... }<\/tt><br \/>\n<tt> Klasse X5 &lt;T erweitert <b> <span style=\"color:#000099;\"> Thread.State<\/span> <\/b> &gt; { ... }<\/tt><br \/>\n<tt> Klasse X6 &lt;T erweitert <b> <span style=\"color:#000099;\"> Liste<\/span> <\/b> &gt; { ... }<\/tt><br \/>\n<tt> Klasse X7 &lt;T erweitert <b> <span style=\"color:#000099;\"> List&lt;String&gt;<\/span> <\/b> &gt; { ... }<\/tt><br \/>\n<tt> Klasse X8 &lt;T erweitert <b> <span style=\"color:#000099;\"> List&lt;? erweitert Number&gt;<\/span> <\/b> &gt; { ... }<\/tt><br \/>\n<tt> Klasse X9 &lt;T erweitert <b> <span style=\"color:#000099;\"> Vergleichbar&lt;? super Number&gt;<\/span> <\/b> &gt; { ... }<\/tt><br \/>\n<tt> Klasse X10&lt;T erweitert <b> <span style=\"color:#000099;\"> Map.Entry&lt;?,?&gt;<\/span> <\/b> &gt; { ... }<\/tt><\/p><\/blockquote>\n<p>Das Codebeispiel zeigt, dass primitive Typen wie z <tt> int<\/tt> und Array-Typen wie <tt> Objekt[] <\/tt> sind als Typparameterbindung nicht zul\u00e4ssig.<\/p>\n<p>Klassentypen, wie z <tt> Nummer<\/tt> oder <tt> Zeichenfolge<\/tt> , und Schnittstellentypen, wie z <tt> Lauff\u00e4hig<\/tt> , sind als Typparameterbindung zul\u00e4ssig.<\/p>\n<p>Aufz\u00e4hlungstypen, wie z <tt> Thread.State<\/tt> sind auch als Typparameterbindung zul\u00e4ssig. <tt> Thread.State<\/tt> ist ein Beispiel f\u00fcr einen verschachtelten Typ, der als Typparameterbindung verwendet wird.\u00a0 Auch nichtstatische Innentypen sind zul\u00e4ssig.<\/p>\n<p>Rohtypen sind als Typparameterbindung zul\u00e4ssig; <tt> <span style=\"color:#000000;\"> Liste<\/span> <\/tt> ist ein Beispiel.<\/p>\n<p>Parametrisierte Typen sind als Typparameterbindung zul\u00e4ssig, einschlie\u00dflich konkreter parametrisierter Typen wie z <tt> List&lt;String&gt;<\/tt> , begrenzte Wildcard-parametrisierte Typen wie z <tt> List&lt;? erweitert Number&gt;<\/tt> Und <tt> Vergleichbar&lt;? super Long&gt;<\/tt> und unbegrenzte, mit Platzhaltern parametrisierte Typen wie z <tt> Map.Entry&lt;?,?&gt;<\/tt> .\u00a0 Eine Grenze, bei der es sich um einen parametrisierten Platzhaltertyp handelt, l\u00e4sst als Typargument alle Typen zu, die zu der Typfamilie geh\u00f6ren, die der Platzhalter bezeichnet.\u00a0 Die durch Platzhalter parametrisierte Typbindung gew\u00e4hrt nur eingeschr\u00e4nkten Zugriff auf Felder und Methoden. Die Einschr\u00e4nkungen h\u00e4ngen von der Art des Platzhalters ab.<\/p>\n<p>Beispiel (f\u00fcr einen mit Platzhalterzeichen parametrisierten Typ als Typparametergrenze):<\/p>\n<blockquote><p><tt> Klasse X&lt;<b> <span style=\"color:#000099;\"> T erweitert List&lt;? erweitert Number&gt;<\/span> <\/b> &gt; {<\/tt><br \/>\n<tt> \u00a0 public void someMethod(T t) {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 t.add(new Long(0L));\u00a0\u00a0\u00a0 <b> <span style=\"color:#660000;\"> \/\/ Fehler<\/span> <\/b> <\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 Zahl n = t.remove(0);<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> }<\/tt><br \/>\n<tt> Klassentest {<\/tt><br \/>\n<tt> \u00a0 public static void main(String[] args) {<\/tt><br \/>\n<tt> <b> <span style=\"color:#660000;\"> \u00a0\u00a0\u00a0\u00a0 <\/span> <\/b> <span style=\"color:#000000;\"> X&lt;ArrayList&lt;<\/span> <b> <span style=\"color:#000099;\"> Lang<\/span> <\/b> <span style=\"color:#000000;\"> &gt;&gt; x1 = new X&lt;ArrayList&lt;Long&gt;&gt;();\u00a0<\/span> <\/tt><br \/>\n<tt> <span style=\"color:#000000;\"> \u00a0\u00a0\u00a0\u00a0 X&lt;ArrayList&lt;<\/span> <b> <span style=\"color:#000099;\"> Zeichenfolge<\/span> <\/b> <span style=\"color:#000000;\"> &gt;&gt; x2 = new X&lt;ArrayList&lt;String&gt;&gt;(); <\/span> <b> <span style=\"color:#660000;\"> \/\/ Fehler<\/span> <\/b> <\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> }<\/tt><\/p><\/blockquote>\n<p>Referenzvariablen vom Typ<tt> T <\/tt> (der Typparameter) werden wie Referenzvariablen eines Wildcard-Typs (der Typparameter gebunden) behandelt.\u00a0 In unserem Beispiel hat dies zur Folge, dass der Compiler den Aufruf von Methoden ablehnt, die ein Argument des \u201eunbekannten\u201c Typs annehmen, f\u00fcr den der Typparameter steht, wie z <tt> List.add<\/tt> , da es sich bei der Grenze um einen mit Platzhaltern parametrisierten Typ mit einer Obergrenze handelt.<br \/>\nGleichzeitig die Grenze <tt> List&lt;? erweitert Number&gt;<\/tt> bestimmt die Typen, die als Typargumente verwendet werden k\u00f6nnen. Der Compiler akzeptiert alle Typargumente, die zur Typfamilie geh\u00f6ren <tt> List&lt;? erweitert Number&gt;<\/tt> , also alle Untertypen von <tt> Liste<\/tt> mit einem Typargument, das ein Subtyp von ist <tt> Nummer<\/tt> .<\/p>\n<p>Ein Typparameter kann als Grenze eines anderen Typparameters verwendet werden.<\/p>\n<p>Beispiel (f\u00fcr einen Typparameter, der als Typparametergrenze verwendet wird):<\/p>\n<blockquote><p><tt> Klasse Triple<b> &lt;T&gt;<\/b> {<\/tt><br \/>\n<tt> \u00a0 privates T fst, snd, trd;<\/tt><br \/>\n<tt> \u00a0 \u00f6ffentlich &lt;<b> U<\/b> <b> <span style=\"color:#000099;\"> erweitert T<\/span> <\/b> , <b> V <span style=\"color:#000099;\"> erweitert T<\/span> <\/b> , <b> W <span style=\"color:#000099;\"> erweitert T<\/span> <\/b> &gt; Triple(U arg1, V arg2, W arg3) {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 fst = arg1;<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 snd = arg2;<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 trd = arg3;<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> }<\/tt><\/p><\/blockquote>\n<p>In diesem Beispiel der Typparameter <tt> T<\/tt> der parametrisierten Klasse wird als Grenze der Typparameter verwendet <tt> U<\/tt> ,\u00a0 <tt> V<\/tt> Und <tt> W<\/tt> einer parametrisierten Instanzmethode dieser Klasse.<\/p>\n<p>Weitere M\u00f6glichkeiten zur Verwendung von Typparametern als Grenzen anderer Typparameter umfassen Situationen, in denen ein verschachtelter Typ innerhalb eines generischen Typs oder eine lokale Klasse innerhalb einer generischen Methode definiert wird.\u00a0 Es ist sogar zul\u00e4ssig, einen Typparameter als Grenze eines anderen Typparameters im selben Typparameterabschnitt zu verwenden.<\/p>\n<p>Eine Grenze, die eine Klasse ist, gew\u00e4hrt Zugriff auf alle ihre \u00f6ffentlichen Mitglieder, also auf \u00f6ffentliche Felder, Methoden und verschachtelte Typen. Nur Konstruktoren werden nicht zug\u00e4nglich gemacht, da es keine Garantie daf\u00fcr gibt, dass eine Unterklasse der Bindung dieselben Konstruktoren wie die Bindung hat.<\/p>\n<p>Beispiel (einer Klasse, die als Grenze eines Typparameters verwendet wird):<\/p>\n<blockquote><p><tt> \u00f6ffentliche Klasse <span style=\"color:#000000;\"> Superklasse<\/span> {<\/tt><tt> \u00a0\u00a0\u00a0 \/\/ statische Mitglieder<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 \u00f6ffentliche Aufz\u00e4hlung EnumType {THIS, Those}<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 \u00f6ffentliches statisches Objekt staticField;<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 public static void staticMethod() { ... }<\/tt><\/p>\n<p><tt> \u00a0\u00a0\u00a0 \/\/ nicht statische Mitglieder<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 \u00f6ffentliche Klasse InnerClass { ... }<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 \u00f6ffentliches Objekt nonStaticField;<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 public void nonStaticMethod() { ... }<\/tt><\/p>\n<p><tt> \u00a0\u00a0\u00a0 \/\/ Konstruktoren<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 public SuperClass() { ... }<\/tt><\/p>\n<p><tt> \u00a0\u00a0\u00a0 \/\/ private Mitglieder<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 privates Objekt privateField;\u00a0<\/tt><\/p>\n<p><tt> \u00a0\u00a0\u00a0 ...<\/tt><br \/>\n<tt> }<\/tt><\/p>\n<p><tt> \u00f6ffentliche Abschlussklasse SomeClass&lt;T erweitert <b> <span style=\"color:#006600;\"> Superklasse<\/span> <\/b> &gt; {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 privates T-Objekt;<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 public SomeClass(T t) { object = t; }\u00a0<\/tt><\/p>\n<p><tt> \u00a0\u00a0\u00a0 \u00f6ffentlicher String toString() {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 zur\u00fcckkehren<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u201estatischer verschachtelter Typ\u00a0\u00a0\u00a0 : \u201e+T.EnumType.class+\u201c\\n\u201c\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 +\"statisches Feld\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : \"+T.staticField+\"\\n\"<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 +\"statische Methode\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : \"+T.staticMethod()+\"\\n\"<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 +\"nicht statischer verschachtelter Typ: \"+T.InnerClass.class+\"\\n\"<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 +\"non-static field\u00a0\u00a0\u00a0\u00a0\u00a0 : \"+object.nonStaticField+\"\\n\"<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 +\"nicht-statische Methode\u00a0\u00a0\u00a0\u00a0 : \"+object.nonStaticMethod()+\"\\n\"<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 +\"constructor\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : \"+(new T())+\"\\n\"\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Fehler<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 +\"private member\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : \"+object.privateField+\"\\n\"\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Fehler\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ;<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 }<\/tt><br \/>\n<tt> }<\/tt><\/p><\/blockquote>\n<p>Die Grenze <tt> Superklasse<\/tt> gew\u00e4hrt Zugriff auf seine verschachtelten Typen, statischen Felder und Methoden sowie nicht statische Felder und Methoden.\u00a0 Nur der Konstruktor ist nicht zug\u00e4nglich.\u00a0 Dies liegt daran, dass Konstruktoren nicht vererbt werden. Jede Unterklasse definiert ihre eigenen Konstruktoren und muss die Konstruktoren ihrer Oberklasse nicht unterst\u00fctzen.\u00a0 Daher gibt es keine Garantie daf\u00fcr, dass eine Unterklasse von <tt> Superklasse<\/tt> wird denselben Konstruktor wie seine Oberklasse haben.<\/p>\n<p>Der Kontext, in dem<tt> Enum&lt;E erweitert Enum&lt;E&gt;&gt;<\/tt> &#8221; erscheint, ist die Erkl\u00e4rung des <tt> Aufz\u00e4hlung<\/tt> Klasse im Paket <tt> java.lang<\/tt> :<\/p>\n<blockquote><p><tt> \u00f6ffentliche abstrakte Klasse <b> Enum&lt;E erweitert Enum&lt;E&gt;&gt;<\/b> {<\/tt><br \/>\n<tt> \u00a0 ...<\/tt><br \/>\n<tt> }<\/tt><\/p><\/blockquote>\n<p>Der Typ <tt> Aufz\u00e4hlung<\/tt> ist die gemeinsame Basisklasse aller Aufz\u00e4hlungstypen.\u00a0 In Java ein Aufz\u00e4hlungstyp wie <tt> Farbe<\/tt> wird in eine Klasse \u00fcbersetzt <tt> Farbe<\/tt> das erstreckt sich <tt> Enum&lt;Color&gt;<\/tt> . Der Zweck der Superklasse <tt> Aufz\u00e4hlung<\/tt> besteht darin, Funktionen bereitzustellen, die allen Aufz\u00e4hlungstypen gemeinsam sind.<\/p>\n<p>Hier ist eine Skizze der Klasse <tt> Aufz\u00e4hlung<\/tt> :<\/p>\n<blockquote><p><tt> \u00f6ffentliche abstrakte Klasse <b> Enum&lt;<span style=\"color:#000099;\"> E<\/span> erweitert Enum&lt;E&gt;&gt;<\/b> implementiert Comparable&lt;<b> <span style=\"color:#000099;\"> E<\/span> <\/b> &gt;, Serialisierbar {<\/tt><br \/>\n<tt> \u00a0 privater endg\u00fcltiger String-Name;<\/tt><br \/>\n<tt> \u00a0 public final String name() { ... }<\/tt><tt> \u00a0 private final int ordinal;<\/tt><br \/>\n<tt> \u00a0 public final int ordinal() { ... }<\/tt><\/p>\n<p><tt> \u00a0 protected Enum(String name, int ordinal) { ... }<\/tt><\/p>\n<p><tt> \u00a0 \u00f6ffentlicher String\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 toString() { ... }<\/tt><br \/>\n<tt> \u00a0 public final boolean equals(Object other) { ... }<\/tt><br \/>\n<tt> \u00a0 public final int hashCode() { ... }<\/tt><br \/>\n<tt> \u00a0 protected final Object clone() l\u00f6st CloneNotSupportedException { ... } aus<\/tt><br \/>\n<tt> \u00a0 public final int CompareTo(<b> <span style=\"color:#000099;\"> E<\/span> <\/b> o) { ... }<\/tt><\/p>\n<p><tt> \u00a0 \u00f6ffentliches Finale Class&lt;<b> <span style=\"color:#000099;\"> E<\/span> <\/b> &gt; getDeclaringClass() { ... }<\/tt><br \/>\n<tt> \u00a0 public static &lt;T erweitert Enum&lt;T&gt;&gt; T valueOf(Class&lt;T&gt; enumType, String name) { ... }<\/tt><br \/>\n<tt> }<\/tt><\/p><\/blockquote>\n<p>Das \u00dcberraschende in der Erkl\u00e4rung \u201e<tt> Enum&lt;E erweitert Enum&lt;E&gt;&gt;<\/tt> &#8221; ist die Tatsache, dass die neu definierte Klasse <tt> Aufz\u00e4hlung<\/tt> und sein neu definierter Typparameter <tt> E <\/tt> erscheinen in der Grenze desselben Typparameters.\u00a0 Es bedeutet, dass die <tt> Aufz\u00e4hlung<\/tt> Der Typ muss f\u00fcr einen seiner Untertypen instanziiert werden.\u00a0 Um zu verstehen, warum dies sinnvoll ist, bedenken Sie, dass jeder Aufz\u00e4hlungstyp in einen Untertyp von \u00fcbersetzt wird <tt> Aufz\u00e4hlung<\/tt> .<\/p>\n<p>Hier ist der erfundene Aufz\u00e4hlungstyp <tt> Farbe<\/tt> :<\/p>\n<blockquote><p><tt> Aufz\u00e4hlung <b> Farbe<\/b> {ROT, BLAU, GR\u00dcN}<\/tt><\/p><\/blockquote>\n<p>Der Compiler \u00fcbersetzt es in die folgende Klasse:<\/p>\n<blockquote><p><tt> \u00f6ffentliche Abschlussklasse <b> Farbe<\/b> erstreckt sich <b> Enum&lt;Color&gt;<\/b> {<\/tt><br \/>\n<tt> \u00a0 public static final Color[] Values() { return (Color[])$VALUES.clone(); }<\/tt><br \/>\n<tt> \u00a0 \u00f6ffentlicher statischer FarbwertOf(String name) { ... }<\/tt><tt> \u00a0 private Color(String s, int i) { super(s, i); }<\/tt><\/p>\n<p><tt> \u00a0 \u00f6ffentliche statische endg\u00fcltige Farbe ROT;<\/tt><br \/>\n<tt> \u00a0 \u00f6ffentliche statische endg\u00fcltige Farbe BLAU;<\/tt><br \/>\n<tt> \u00a0 \u00f6ffentliche statische endg\u00fcltige Farbe GR\u00dcN;<\/tt><\/p>\n<p><tt> \u00a0 private statische endg\u00fcltige Farbe $VALUES[];<\/tt><\/p>\n<p><tt> \u00a0 statisch {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 ROT = neue Farbe(\"RED\", 0);<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 BLAU = neue Farbe(\"BLAU\", 1);<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 GR\u00dcN = neue Farbe(\"GR\u00dcN\", 2);<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 $VALUES = (neue Farbe[] { ROT, BLAU, GR\u00dcN });<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> }<\/tt><\/p><\/blockquote>\n<p>Die Vererbung bewirkt, dass die <tt> Farbe<\/tt> Typ erbt alle in implementierten Methoden <tt> Enum&lt;Color&gt;<\/tt> .\u00a0 Unter ihnen ist die <tt> vergleichenMit<\/tt> Verfahren.\u00a0 Der <tt> Farbe. vergleichenMit<\/tt> Methode sollte wahrscheinlich eine dauern <tt> Farbe<\/tt> als Argument.\u00a0 Um dies zu erm\u00f6glichen, Klasse <tt> Aufz\u00e4hlung<\/tt> ist generisch und die <tt> Enum.compareTo<\/tt> Methode dauert <tt> Aufz\u00e4hlung<\/tt> Typparameter <tt> E <\/tt> als Argument.\u00a0 Geben Sie als Ergebnis ein <tt> Farbe<\/tt> abgeleitet von <tt> Enum&lt;Color&gt;<\/tt> erbt a <tt> vergleichenMit<\/tt> Methode, die eine dauert <tt> Farbe<\/tt> as und Argument, genau so, wie es sollte.<br \/>\nWenn wir die Erkl\u00e4rung analysieren:<tt> Enum&lt;E erweitert Enum&lt;E&gt;&gt;<\/tt> \u201eWir k\u00f6nnen sehen, dass dieses Muster mehrere Aspekte hat.<\/p>\n<p>Erstens gibt es die Tatsache, dass der Typparameter, der gebunden wird, der Typ selbst ist: \u201e<tt> <b> Aufz\u00e4hlung<\/b> &lt;E erweitert <b> Aufz\u00e4hlung<\/b> &lt;E&gt;&gt;<\/tt> &#8220;. Es stellt sicher, dass nur Untertypen des Typs vorhanden sind <tt> Aufz\u00e4hlung<\/tt> sind als Typargumente zul\u00e4ssig. (Theoretisch, Typ <tt> Aufz\u00e4hlung<\/tt> k\u00f6nnte auf sich selbst instanziiert werden, wie in <tt> Enum&lt;Enum&gt;<\/tt> , aber das ist sicherlich nicht beabsichtigt und es ist schwer, sich eine Situation vorzustellen, in der eine solche Instanziierung n\u00fctzlich w\u00e4re.)<\/p>\n<p>Zweitens gibt es die Tatsache, dass der Parameter-gebundene Typ der parametrisierte Typ ist <tt> Aufz\u00e4hlung<b> &lt;E&gt;<\/b> <\/tt> , das den Typparameter verwendet <tt> E<\/tt> als Typargument der Grenze. Diese Deklaration stellt sicher, dass die Vererbungsbeziehung zwischen einem Subtyp und einer Instanziierung von <tt> Aufz\u00e4hlung<\/tt> ist von der Form<tt> X erweitert Enum&lt;X&gt;<\/tt> &#8220;. Ein Untertyp wie \u201e<tt> X erweitert Enum&lt;Y&gt;<\/tt> &#8221; kann aufgrund des Typarguments nicht deklariert werden <tt> Y<\/tt> w\u00fcrde nicht innerhalb der Grenzen liegen; nur Unterarten von <tt> Enum&lt;X&gt;<\/tt> liegen im Rahmen.<\/p>\n<p>Drittens gibt es die Tatsache, dass <tt> Aufz\u00e4hlung<\/tt> ist in erster Linie generisch.\u00a0 Dies bedeutet, dass einige Methoden der Klasse <tt> Aufz\u00e4hlung<\/tt> Nehmen Sie ein Argument an oder geben Sie einen Wert eines unbekannten Typs zur\u00fcck (oder h\u00e4ngen Sie auf andere Weise von einem unbekannten Typ ab). Wie wir bereits wissen, wird dieser unbekannte Typ sp\u00e4ter ein Untertyp sein <tt> X<\/tt> von <tt> Enum&lt;X&gt;<\/tt> .\u00a0 Daher im parametrisierten Typ <tt> Enum&lt;X&gt;<\/tt> , diese Methoden beinhalten den Untertyp <tt> X<\/tt> , und sie werden in den Untertyp vererbt <tt> X<\/tt> .\u00a0 Der <tt> vergleichenMit<\/tt> Methode ist ein Beispiel f\u00fcr eine solche Methode; Es wird von der Oberklasse in jede Unterklasse vererbt und verf\u00fcgt jeweils \u00fcber eine unterklassenspezifische Signatur.<\/p>\n<p>Um es zusammenzufassen: Die Erkl\u00e4rung \u201e<tt> Enum&lt;E erweitert Enum&lt;E&gt;&gt;<\/tt> \u201ekann entziffert werden als: <tt> Aufz\u00e4hlung<\/tt> ist ein generischer Typ, der nur f\u00fcr seine Untertypen instanziiert werden kann, und diese Untertypen erben einige n\u00fctzliche Methoden, von denen einige untertypspezifische Argumente annehmen (oder auf andere Weise vom Untertyp abh\u00e4ngen).<\/p>\n<p><b>Typparameter der Untergrenze von Klassen<\/b><\/p>\n<p>Typparameter k\u00f6nnen mehrere Grenzen haben, wie in <tt> Klasse Box&lt;T erweitert Number&gt; {...}<\/tt> . Ein Typparameter kann jedoch keine untere Grenze haben, d. h. ein Konstrukt wie <tt> Klasse Box&lt;T <b> Gro\u00dfartig<\/b> Nummer&gt; {...}<\/tt> ist nicht gestattet.\u00a0 Warum nicht?\u00a0 Die Antwort lautet: Es ist sinnlos, weil man damit nichts kaufen w\u00fcrde, wenn es erlaubt w\u00e4re.\u00a0 Lassen Sie uns sehen, warum die Untergrenze von Typparametern von Klassen verwirrend ist, indem wir untersuchen, was eine Obergrenze f\u00fcr einen Typparameter bedeutet.<\/p>\n<p>Die Obergrenze f\u00fcr einen Typparameter hat drei Auswirkungen:<\/p>\n<ol>\n<ol>\n<li><b> <i> Eingeschr\u00e4nkte Instanziierung.<\/i> <\/b> Die Obergrenze schr\u00e4nkt die Menge der Typen ein, die zur Instanziierung des generischen Typs verwendet werden k\u00f6nnen.\u00a0 Wenn wir a erkl\u00e4ren <tt> Klasse Box&lt;T erweitert Number&gt; {...} <\/tt> dann w\u00fcrde der Compiler sicherstellen, dass nur Untertypen von <tt> Nummer<\/tt> kann als Typargument verwendet werden.\u00a0 Das hei\u00dft, a <tt> Box&lt;Number&gt;<\/tt> oder ein <tt> Box&lt;Long&gt;<\/tt> ist erlaubt, aber a <tt> Box&lt;Object&gt;<\/tt> oder <tt> Box&lt;String&gt;<\/tt> w\u00fcrde abgelehnt werden<\/li>\n<\/ol>\n<\/ol>\n<p>Beispiel (f\u00fcr eine eingeschr\u00e4nkte Instanziierung aufgrund einer Obergrenze f\u00fcr einen Typparameter):<br \/>\n<tt> Klasse Box&lt;T erweitert Number&gt; {\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0 privater T-Wert;\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0 public Box(T t) { value = t; }\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0 ...<\/tt><br \/>\n<tt> }\u00a0<\/tt><tt> Klassentest {\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0 public static void main(String[] args) {\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Box&lt;Long&gt; boxOfLong = neue Box&lt;Long&gt;(0L);\u00a0\u00a0 <b> \/\/ Bu\u00dfgeld<\/b> <\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Box&lt;String&gt; boxOfString = new Box&lt;String&gt;(\"\"); <b> <span style=\"color:#330000;\"> \/\/ Fehler: String liegt nicht innerhalb der Grenzen<\/span> <\/b> <\/tt><br \/>\n<tt> \u00a0\u00a0 }<\/tt><br \/>\n<tt> }<\/tt><\/p>\n<ol>\n<li><b> <i> Zugriff auf nicht-statische Mitglieder. <\/i> <\/b> Die Obergrenze erm\u00f6glicht den Zugriff auf alle \u00f6ffentlichen nicht statischen Methoden und Felder der Obergrenze.\u00a0 Bei der Umsetzung unserer <tt> Klasse Box&lt;T erweitert Number&gt; {...}<\/tt> Wir k\u00f6nnen alle in der Klasse definierten \u00f6ffentlichen nicht statischen Methoden aufrufen <tt> Nummer<\/tt> , wie zum Beispiel <tt> intValue()<\/tt> zum Beispiel.\u00a0 Ohne die Obergrenze w\u00fcrde der Compiler jeden solchen Aufruf ablehnen. Beispiel (f\u00fcr den Zugriff auf nicht statische Mitglieder aufgrund einer Obergrenze f\u00fcr einen Typparameter):<\/li>\n<\/ol>\n<p><tt> Klasse Box&lt;T erweitert Number&gt; {\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0 privater T-Wert;\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0 public Box(T t) { value = t; }\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0 public int increment() { return value.intValue()+1; } <b> \/\/ &lt;= w\u00e4re ein Fehler ohne die Zahl gebunden<\/b> <\/tt><br \/>\n<tt> \u00a0\u00a0 ...<\/tt><br \/>\n<tt> }\u00a0<\/tt><\/p>\n<ol>\n<ol>\n<li><b> <i> Geben Sie \u201eL\u00f6schen\u201c ein. <\/i> <\/b> Die Obergrenze ganz links wird zum L\u00f6schen des Typs verwendet und ersetzt den Typparameter im Bytecode.\u00a0 In unserem <tt> Klasse Box&lt;T erweitert Number&gt; {...}<\/tt> alle Vorkommen von <tt> T<\/tt> w\u00fcrde durch die Obergrenze ersetzt werden <tt> Nummer<\/tt> .\u00a0 Zum Beispiel, wenn Klasse <tt> Kasten<\/tt> hat ein privates Feld vom Typ <tt> T<\/tt> und eine Methode <tt> Leere Menge (T-Inhalt) <\/tt> zum Festlegen dieses privaten Felds w\u00e4re das Feld vom Typ <tt> Nummer<\/tt> nach der Typl\u00f6schung w\u00fcrde die Methode in eine Methode \u00fcbersetzt <tt> void set(Zahleninhalt)<\/tt> .<\/li>\n<\/ol>\n<\/ol>\n<p>Beispiel (f\u00fcr die Verwendung einer Obergrenze f\u00fcr einen Typparameter bei der Typl\u00f6schung \u2013 <i> vor<\/i> Typ L\u00f6schung):<br \/>\n<tt> Klasse Box&lt;<b> T erweitert Zahl<\/b> &gt; {\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0 Privat <b> T<\/b> Wert;\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0 \u00f6ffentliche Box(<b> T<\/b> t) { Wert = t; }\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0 ...<\/tt><br \/>\n<tt> }\u00a0<\/tt><\/p>\n<ol>\n<ol>Beispiel (f\u00fcr die Verwendung einer Obergrenze f\u00fcr einen Typparameter bei der Typl\u00f6schung \u2013<\/ol>\n<\/ol>\n<p><i> nach<\/i><\/p>\n<ol>\n<ol>Typ L\u00f6schung):<\/ol>\n<\/ol>\n<p>&nbsp;<\/p>\n<p><tt> Klasse Box {\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0 Privat <b> Nummer<\/b> Wert;\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0 \u00f6ffentliche Box(<b> Nummer<\/b> t) { Wert = t; }\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0 ...<\/tt><br \/>\n<tt> }<\/tt><\/p>\n<ol>Dar\u00fcber hinaus erscheint die Obergrenze ganz links an weiteren Stellen, beispielsweise bei automatisch eingef\u00fcgten Casts und Bridge-Methoden.<\/ol>\n<p>Welche Nebenwirkungen h\u00e4tten oder sollten sie haben, wenn Untergrenzen f\u00fcr Typparameter zul\u00e4ssig w\u00e4ren? Wenn ein Konstrukt wie <tt> Klasse Box&lt;T <b> Gro\u00dfartig<\/b> Nummer&gt; {...} <\/tt> erlaubt w\u00e4ren, was w\u00fcrde das bedeuten?\u00a0 Was w\u00fcrden die drei Nebenwirkungen einer oberen Typparametergrenze \u2013 eingeschr\u00e4nkte Instanziierung, Zugriff auf nicht-statische Member, Typl\u00f6schung \u2013 f\u00fcr eine untere Grenze bedeuten?<\/p>\n<p><i> Eingeschr\u00e4nkte Instanziierungen. <\/i> Der Compiler k\u00f6nnte die Menge der Typen, die f\u00fcr die Instanziierung des generischen Typs verwendet werden k\u00f6nnen, mit einem Typparameter mit unterer Grenze einschr\u00e4nken.\u00a0 Der Compiler k\u00f6nnte beispielsweise Instanziierungen zulassen wie <tt> Box&lt;Number&gt;<\/tt> Und <tt> Box&lt;Object&gt;<\/tt> von a <tt> Box&lt;T <b> Gro\u00dfartig<\/b> Nummer&gt; <\/tt> und lehnen Sie Instanziierungen wie ab <tt> Box&lt;Long&gt;<\/tt> oder <tt> Box&lt;Short&gt;<\/tt> .\u00a0 Dies w\u00e4re ein Effekt, der mit dem restriktiven Nebeneffekt \u00fcbereinstimmt, der f\u00fcr obere Typparametergrenzen beschrieben wurde.<\/p>\n<p><i> Zugriff auf nicht-statische Mitglieder. <\/i> Eine niedrigere Typparametergrenze gew\u00e4hrt keinen Zugriff auf bestimmte Methoden, die \u00fcber die von der Klasse geerbten Methoden hinausgehen <tt> Objekt<\/tt> . Am Beispiel von <tt> Box&lt;T <b> Gro\u00dfartig<\/b> Nummer&gt; <\/tt> die Supertypen von <tt> Nummer<\/tt> haben nichts gemeinsam, au\u00dfer dass sie Referenztypen und daher Untertypen von sind <tt> Objekt<\/tt> .\u00a0 Der Compiler kann nicht davon ausgehen, dass das Feld vom Typ ist <tt> T<\/tt> ist vom Typ <tt> Nummer<\/tt> oder eine Unterart davon.\u00a0 Stattdessen das Feld vom Typ <tt> T<\/tt> kann von jedem Supertyp sein <tt> Nummer<\/tt> , wie zum Beispiel <tt> Serialisierbar<\/tt> oder <tt> Objekt<\/tt> .\u00a0 Der Aufruf einer Methode wie z <tt> intValue()<\/tt> ist nicht mehr typsicher und der Compiler m\u00fcsste es ablehnen. Infolgedessen w\u00fcrde die untere Typparametergrenze keinen Zugriff auf nicht statische Mitglieder erm\u00f6glichen, die \u00fcber die in der Klasse definierten hinausgehen <tt> Objekt<\/tt> und hat somit die gleiche Wirkung wie \u201enobound\u201c.<\/p>\n<p><i> Geben Sie \u201eL\u00f6schen\u201c ein<\/i> .\u00a0 Nach dieser Logik macht es keinen Sinn, die Vorkommen des Typparameters durch seine untere Grenze ganz links zu ersetzen.\u00a0 Deklarieren einer Methode wie des Konstruktors <tt> Kasten(<b> T<\/b> T) <\/tt> als Konstrukteur <tt> Kasten(<b> Nummer<\/b> T)<\/tt> macht in Anbetracht dessen keinen Sinn <tt> T<\/tt> wird durch einen Supertyp von ersetzt <tt> Nummer<\/tt> .\u00a0 Ein <tt> Objekt<\/tt> k\u00f6nnte in einer Instanziierung korrekt an den Konstruktor \u00fcbergeben werden <tt> Box&lt;Object&gt;<\/tt> und der Konstruktor w\u00fcrde es ablehnen.\u00a0 Das w\u00e4re absolut falsch.\u00a0\u00a0 Die Typl\u00f6schung w\u00fcrde also alle Vorkommen der Typvariablen ersetzen<tt> T<\/tt> nach Typ <tt> Objekt<\/tt> und nicht durch seine Untergrenze.\u00a0 Auch hier h\u00e4tte die Untergrenze den gleichen Effekt wie \u201ekeine Grenze\u201c.<\/p>\n<p>M\u00f6chten Sie herausfinden, was es bedeuten w\u00fcrde, wenn beide niedriger w\u00e4ren? <i> _Und_<\/i> Obergrenzen waren erlaubt?\u00a0\u00a0 Pers\u00f6nlich m\u00f6chte ich gar nicht dar\u00fcber nachdenken und w\u00fcrde es lieber unter \u201enicht beherrschbar\u201c einordnen, wenn Sie es erlauben.<\/p>\n<p>Die Quintessenz ist: Das ist alles ein \u201e<tt> Gro\u00dfartig<\/tt> \u201ebound would buy you\u201c ist die Einschr\u00e4nkung, die nur Supertypen davon haben <tt> Nummer<\/tt> k\u00f6nnen als Typargumente verwendet werden.\u00a0 Und selbst das wird h\u00e4ufig missverstanden.\u00a0 Das w\u00fcrde es NICHT bedeuten <tt> Klasse Box&lt;T super Number&gt; {...}<\/tt> enth\u00e4lt nur Instanzen von Supertypen von <tt> Nummer<\/tt> .\u00a0 Ganz im Gegenteil \u2013 wie das folgende Beispiel zeigt!<\/p>\n<p>Beispiel (f\u00fcr die Verwendung einer Obergrenze f\u00fcr einen Typparameter bei der Typl\u00f6schung \u2013 <i> vor<\/i> Typ L\u00f6schung):<\/p>\n<div style=\"margin-left:40px;\">\n<blockquote><p><tt> Klasse Box&lt;<b> T-Super-Nummer<\/b> &gt; {<\/tt><br \/>\n<tt> \u00a0\u00a0 Privat <b> T<\/b> Wert;<\/tt><br \/>\n<tt> \u00a0\u00a0 \u00f6ffentliche Box(<b> T<\/b> t) { Wert = t; }<\/tt><br \/>\n<tt> \u00a0\u00a0 ...<\/tt><br \/>\n<tt> }<\/tt><\/p><\/blockquote>\n<\/div>\n<p>Beispiel (f\u00fcr die Verwendung einer Obergrenze f\u00fcr einen Typparameter bei der Typl\u00f6schung \u2013 <i> nach<\/i> Typ L\u00f6schung):<\/p>\n<div style=\"margin-left:40px;\">\n<blockquote><p><tt> Klasse Box {<\/tt><br \/>\n<tt> \u00a0\u00a0 Privat <b> Objekt<\/b> Wert;<\/tt><br \/>\n<tt> \u00a0\u00a0 \u00f6ffentliche Box(<b> Objekt<\/b> t) { Wert = t; }<\/tt><br \/>\n<tt> \u00a0\u00a0 ...<\/tt><br \/>\n<tt> }<\/tt><\/p><\/blockquote>\n<\/div>\n<p>A <tt> Klasse Box&lt;T <b> Gro\u00dfartig<\/b> Nummer&gt; {...} <\/tt> w\u00fcrde durch Typl\u00f6schung in a \u00fcbersetzt werden <tt> Kasten<\/tt> enth\u00e4lt eine <tt> Objekt<\/tt> Feld und sein Konstruktor w\u00fcrden \u00fcbersetzt werden <tt> Box(Objekt t)<\/tt> . Das unterscheidet sich grundlegend von a <tt> Klasse Box&lt;T <b> erstreckt sich<\/b> Nummer&gt; {...}<\/tt> , was \u00fcbersetzt werden w\u00fcrde zu a <tt> Kasten<\/tt> enth\u00e4lt a <tt> Nummer<\/tt> Feld und sein Konstruktor w\u00fcrden \u00fcbersetzt werden <tt> Box (Anzahl t)<\/tt> . Folglich a <tt> Box&lt;Number&gt;<\/tt> instanziiert von a<tt> Klasse Box&lt;T erweitert Number&gt; {...}<\/tt> w\u00e4re anders als a <tt> Box&lt;Number&gt;<\/tt> Instanziierung von a <tt> Klasse Box&lt;T super Number&gt; {...}<\/tt> , was wahrscheinlich Verwirrung stiften wird.\u00a0 Aus diesem Grund sind Untergrenzen f\u00fcr Typparameter von Klassen nicht sinnvoll.<\/p>\n<p>&nbsp;<\/p>\n<hr width=\"100%\" \/>\n<p>&nbsp;<\/p>\n<p><b>Typparameter der Untergrenze von Methoden<\/b><\/p>\n<p>Im Zusammenhang mit Methoden und ihren Argumenttypen kann gelegentlich ein Typparameter mit einer unteren Grenze n\u00fctzlich sein.<\/p>\n<p>Beispiel (f\u00fcr eine Methode, die von einem Typparameter mit einer Untergrenze profitieren w\u00fcrde):<\/p>\n<blockquote><p><tt> Klassenpaar&lt;X,Y&gt; {<\/tt><br \/>\n<tt> \u00a0\u00a0 privates X zuerst;<\/tt><br \/>\n<tt> \u00a0\u00a0 privat Y Sekunde;<\/tt><br \/>\n<tt> \u00a0\u00a0 ...<\/tt><br \/>\n<tt> \u00a0\u00a0 \u00f6ffentlich &lt;<b> Ein super X<\/b> ,<b> B super Y<\/b> &gt; B addToMap(Map&lt;A,B&gt; map) { \/\/ Fehler: Typparameter darf keine untere Grenze haben<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0 return map.put(first, second);<\/tt><br \/>\n<tt> \u00a0\u00a0 }<\/tt><br \/>\n<tt> }<\/tt><br \/>\n<tt> Klassentest {\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0 public static void main(String[] args) {\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0 Pair&lt;String,Long&gt; Paar = neues Pair&lt;&gt;(\"ABC\",42L);<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0 Map&lt;CharSequence, Number&gt; map = HashMap&lt;CharSequence, Number&gt;();<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0 Nummer number = pair.addToMap(map);<\/tt><br \/>\n<tt> \u00a0\u00a0 }<\/tt><br \/>\n<tt> }<\/tt><\/p><\/blockquote>\n<p>Der <tt> addToMap()<\/tt> Die Methode f\u00fcgt den Inhalt des Paares einer Karte hinzu.\u00a0 Jede Karte, die Supertypen von enthalten kann <tt> X<\/tt> Und <tt> Y<\/tt> w\u00fcrde tun.\u00a0 Die Karte <tt> setzen()<\/tt> Die Methode gibt den in der Karte gefundenen Wert f\u00fcr den angegebenen Schl\u00fcssel zur\u00fcck, wenn in der Karte bereits ein Schl\u00fcsselwerteintrag f\u00fcr den Schl\u00fcssel vorhanden ist.\u00a0 Der R\u00fcckgabewert der Karte <tt> setzen()<\/tt> Die Methode soll von zur\u00fcckgegeben werden <tt> addToMap()<\/tt> Verfahren.\u00a0 Unter diesen Umst\u00e4nden m\u00f6chte man die Methode wie oben gezeigt deklarieren: Die Karte wird mit Supertypen der Typparameter des Paars und der parametrisiert <tt> addToMap()<\/tt> Der R\u00fcckgabetyp der Methode ist der Werttyp der Karte.<\/p>\n<p>Da der Compiler keine unteren Grenzen f\u00fcr Typparameter zul\u00e4sst, ben\u00f6tigen wir eine Problemumgehung.<\/p>\n<p>Eine L\u00f6sung, die mir in den Sinn kommt, ist die Verwendung eines Platzhalters, da Platzhalter eine Untergrenze haben k\u00f6nnen.\u00a0 Hier ist eine Problemumgehung mit einem Platzhalter.<\/p>\n<p>Beispiel (eines Workarounds f\u00fcr das vorherige Beispiel mit Platzhaltern):<\/p>\n<blockquote><p><tt> Klassenpaar&lt;X,Y&gt; {<\/tt><br \/>\n<tt> \u00a0\u00a0 privates X zuerst;<\/tt><br \/>\n<tt> \u00a0\u00a0 privat Y Sekunde;<\/tt><br \/>\n<tt> \u00a0\u00a0 ...<\/tt><br \/>\n<tt> \u00a0\u00a0 \u00f6ffentlich <b> Objekt<\/b> addToMap(Map&lt;<b> ? Super X<\/b> , <b> ? Super Y<\/b> &gt;-Karte) {\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0 return map.put(first, second);<\/tt><br \/>\n<tt> \u00a0\u00a0 }<\/tt><br \/>\n<tt> }<\/tt><br \/>\n<tt> Klassentest {\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0 public static void main(String[] args) {\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0 Pair&lt;String,Long&gt; Paar = neues Pair&lt;&gt;(\"ABC\",42L);<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0 Map&lt;CharSequence, Number&gt; map = HashMap&lt;CharSequence, Number&gt;();<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0 Zahl Zahl = <b> (Nummer)<\/b> pair.addToMap(map);<\/tt><br \/>\n<tt> \u00a0\u00a0 }<\/tt><br \/>\n<tt> }<\/tt><\/p><\/blockquote>\n<p>Es funktioniert, au\u00dfer dass es keine M\u00f6glichkeit gibt, den R\u00fcckgabetyp wie gew\u00fcnscht zu deklarieren.\u00a0 Es w\u00e4re der Supertyp von <tt> Y<\/tt> den der Compiler aus dem Kartentyp erfasst, es gibt jedoch keine Syntax f\u00fcr die Angabe.\u00a0 Wir d\u00fcrfen den R\u00fcckgabetyp nicht als \u201edeklarieren\u201c.<tt> ? Super Y<\/tt> &#8220;, Weil &#8220;<tt> ? Super Y<\/tt> \u201e ist ein Platzhalter und kein Typ und daher als R\u00fcckgabetyp nicht zul\u00e4ssig. Wir haben keine Wahl und m\u00fcssen verwenden <tt> Objekt<\/tt> stattdessen als R\u00fcckgabetyp unserer Methode.\u00a0 Dieser eher unspezifische R\u00fcckgabetyp zwingt wiederum Aufrufer des <tt> addToMap()<\/tt> Methode, um den R\u00fcckgabewert von herunterzuwandeln <tt> Objekt<\/tt> zu seinem eigentlichen Typ.\u00a0 Das ist nicht genau das, was wir uns vorgestellt hatten.<\/p>\n<p>Eine weitere Problemumgehung ist die Verwendung statischer Methoden.\u00a0 Hier ist eine Problemumgehung mit einer statischen statt einer nicht statischen Methode.<\/p>\n<p>Beispiel (eines Workarounds f\u00fcr das vorherige Beispiel mit einer statischen Methode):<\/p>\n<blockquote><p><tt> Klassenpaar&lt;X,Y&gt; {<\/tt><br \/>\n<tt> \u00a0\u00a0 privates X zuerst;<\/tt><br \/>\n<tt> \u00a0\u00a0 privat Y Sekunde;<\/tt><br \/>\n<tt> \u00a0\u00a0 ...<\/tt><br \/>\n<tt> \u00a0\u00a0 \u00f6ffentlich <b> statisch<\/b> &lt;<b> A<\/b> ,<b> B<\/b> ,X <b> erweitert A<\/b> ,Y <b> erweitert B<\/b> &gt; B addToMap(Pair&lt;X,Y&gt; Paar, Map&lt;<b> A<\/b> ,<b> B<\/b> &gt;-Karte) {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0 return map.put(pair.first,pair.second);<\/tt><br \/>\n<tt> \u00a0\u00a0 }<\/tt><br \/>\n<tt> }<\/tt><br \/>\n<tt> Klassentest {\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0 public static void main(String[] args) {\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0 Pair&lt;String,Long&gt; Paar = neues Pair&lt;&gt;(\"ABC\",42L);<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0 Map&lt;CharSequence, Number&gt; map = HashMap&lt;CharSequence, Number&gt;();<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0 Zahl Zahl = <b> Paar<\/b> .addToMap(<b> Paar<\/b> ,Karte);<\/tt><br \/>\n<tt> \u00a0\u00a0 }<\/tt><br \/>\n<tt> }<\/tt><\/p><\/blockquote>\n<p>Das Generikum <tt> addToMap()<\/tt> Die Methode verf\u00fcgt \u00fcber vier Typparameter: zwei Platzhalter <tt> X<\/tt> Und <tt> Y<\/tt> f\u00fcr den Typ des Paares und zwei Platzhalter <tt> A<\/tt> Und <tt> B <\/tt> f\u00fcr den Kartentyp. <tt> A<\/tt> Und <tt> B<\/tt> sind Supertypen von <tt> X<\/tt> Und <tt> Y<\/tt> , Weil <tt> X<\/tt> Und <tt> Y<\/tt> \u00a0 werden mit deklariert <tt> A<\/tt> Und <tt> B<\/tt> als ihre Obergrenzen. (Beachten Sie, dass die Typparameter der generischen Methode <tt> X<\/tt> Und <tt> Y<\/tt> habe nichts damit zu tun <tt> Paar<\/tt> Klasse <tt> X<\/tt> Und <tt> Y<\/tt> Parameter.\u00a0 Die Namen <tt> X<\/tt> Und <tt> Y<\/tt> werden f\u00fcr die generische Methode wiederverwendet, um sie leicht als Typparameter des Paares erkennbar zu machen. Mithilfe von vier Typparametern k\u00f6nnen wir den genauen R\u00fcckgabetyp wie gew\u00fcnscht deklarieren: Es ist derselbe Typ wie der Werttyp der Karte.<\/p>\n<p>Beispiel (f\u00fcr die illegale Verwendung eines Typparameters im statischen Kontext einer generischen Klasse):<\/p>\n<blockquote><p><tt> Klasse SomeClass<b> <span style=\"color:#000099;\"> &lt;T&gt;<\/span> <\/b> {<\/tt><br \/>\n<tt> \u00a0 \/\/ <\/tt> <i> statischer Initialisierer, statisches Feld, statische Methode<\/i><br \/>\n<tt> \u00a0 <b> statisch<\/b> {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 SomeClass&lt;<b> <span style=\"color:#000099;\"> T<\/span> <\/b> &gt;-Test = neues SomeClass&lt;<b> <span style=\"color:#000099;\"> T<\/span> <\/b> &gt;(); <b> <span style=\"color:#660000;\"> \/\/ Fehler<\/span> <\/b> <\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 Privat <b> statisch<\/b> <b> <span style=\"color:#000099;\"> T<\/span> <\/b> globalInfo;\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 <b> <span style=\"color:#660000;\"> \/\/ Fehler<\/span> <\/b> <\/tt><br \/>\n<tt> \u00a0 \u00f6ffentlich\u00a0 <b> statisch<\/b> <b> <span style=\"color:#000099;\"> T <\/span> <\/b> getGlobalInfo() {\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 <b> <span style=\"color:#660000;\"> \/\/ Fehler<\/span> <\/b> <\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 return globalInfo;<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 \/\/ <\/tt> <i> nicht statischer Initialisierer, nicht statisches Feld, nicht statische Methode<\/i><br \/>\n<tt> \u00a0 {\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 SomeClass&lt;<b> <span style=\"color:#000099;\"> T<\/span> <\/b> &gt;-Test = neues SomeClass&lt;<b> <span style=\"color:#000099;\"> T<\/span> <\/b> &gt;();<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 Privat\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 <b> <span style=\"color:#000099;\"> T<\/span> <\/b> localInfo;<\/tt><br \/>\n<tt> \u00a0 \u00f6ffentlich\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 <b> <span style=\"color:#000099;\"> T<\/span> <\/b> getLocalInfo() {\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 return localInfo;<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 \/\/ <\/tt> <i> statische verschachtelte Typen<\/i><br \/>\n<tt> \u00a0 \u00f6ffentlich <b> statisch<\/b> <b> Klasse<\/b> Fehler verl\u00e4ngert Ausnahme {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 Privates Finale <b> <span style=\"color:#000099;\"> T<\/span> <\/b> Info;\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 <b> <span style=\"color:#660000;\"> \/\/ Fehler<\/span> <\/b> <\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 \u00f6ffentliches Versagen(<b> <span style=\"color:#000099;\"> T<\/span> <\/b> t) { info = t; }\u00a0\u00a0\u00a0\u00a0\u00a0 <b> <span style=\"color:#660000;\"> \/\/ Fehler<\/span> <\/b> <\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 \u00f6ffentlich <b> <span style=\"color:#000099;\"> T<\/span> <\/b> getInfo() { return info; }\u00a0\u00a0\u00a0 <b> <span style=\"color:#660000;\"> \/\/ Fehler<\/span> <\/b> <\/tt><br \/>\n<tt> \u00a0\u00a0 }<\/tt><br \/>\n<tt> \u00a0 Privat <b> Schnittstelle<\/b> Kopierbar {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 <b> <span style=\"color:#000099;\"> T<\/span> <\/b> Kopie();\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 <b> <span style=\"color:#660000;\"> \/\/ Fehler<\/span> <\/b> <\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 Privat <b> Aufz\u00e4hlung<\/b> Zustand {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 G\u00dcLTIG, UNG\u00dcLTIG;<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 Privat <b> <span style=\"color:#000099;\"> T<\/span> <\/b> Info; \/\/ Fehler<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 public void setInfo(<b> <span style=\"color:#000099;\"> T<\/span> <\/b> t) { info = t; } <b> <span style=\"color:#660000;\"> \/\/ Fehler<\/span> <\/b> <\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 \u00f6ffentlich <b> <span style=\"color:#000099;\"> T<\/span> <\/b> getInfo() { return info; }\u00a0\u00a0\u00a0 <b> <span style=\"color:#660000;\"> \/\/ Fehler<\/span> <\/b> <\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 \/\/ <\/tt> <i> nicht statische verschachtelte Typen<\/i><br \/>\n<tt> \u00a0 \u00f6ffentlich <b> Klasse<\/b> Zugriffsberechtigter {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 \u00f6ffentlich <b> <span style=\"color:#000099;\"> T<\/span> <\/b> getInfo() { return localInfo; }<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> }<\/tt><\/p><\/blockquote>\n<p>Das Beispiel zeigt, dass der Typparameter nicht im statischen Kontext einer generischen Klasse verwendet werden kann.\u00a0 Es zeigt auch, dass verschachtelte Schnittstellen und Aufz\u00e4hlungstypen als statische Typmitglieder der Klasse gelten.\u00a0 Nur innere Klassen, also nicht statische verschachtelte Klassen, k\u00f6nnen den Typparameter der umschlie\u00dfenden generischen Klasse verwenden.<\/p>\n<p>Der Geltungsbereich des Typparameters einer Schnittstelle umfasst die gesamte Definition der Schnittstelle, mit Ausnahme aller Felder oder verschachtelter Typen.\u00a0 Dies liegt daran, dass in einer Schnittstelle definierte Felder und verschachtelte Typen implizit statisch sind.<\/p>\n<p>Beispiel (f\u00fcr die illegale Verwendung eines Typparameters in einer generischen Schnittstelle):<\/p>\n<blockquote><p><tt> Schnittstelle SomeInterface<b> <span style=\"color:#000099;\"> &lt;T&gt;<\/span> <\/b> {<\/tt><br \/>\n<tt> \u00a0 \/\/ <\/tt> <i> Feld<\/i><br \/>\n<tt> \u00a0 SomeClass&lt;<b> <span style=\"color:#000099;\"> T<\/span> <\/b> &gt;-Wert = neuer SomeClass&lt;<b> <span style=\"color:#000099;\"> T<\/span> <\/b> &gt;();\u00a0 <b> <span style=\"color:#660000;\"> \/\/ Fehler<\/span> <\/b> <\/tt><br \/>\n<tt> \u00a0 \/\/ <\/tt> <i> verschachtelter Typ<\/i><br \/>\n<tt> \u00a0 <b> Klasse<\/b> Zugriffsberechtigter {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 \u00f6ffentlich <b> <span style=\"color:#000099;\"> T <\/span> <\/b> getInfo() {\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 <b> <span style=\"color:#660000;\"> \/\/ Fehler<\/span> <\/b> <\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0 R\u00fcckgabewert.getGlobalInfo();<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0 }<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 \/\/ <\/tt> <i> Methoden<\/i><br \/>\n<tt> \u00a0 <b> <span style=\"color:#000099;\"> T<\/span> <\/b> getValue();<\/tt><br \/>\n<tt> }<\/tt><\/p><\/blockquote>\n<p>Das Beispiel zeigt, dass Felder einer Schnittstelle implizit statisch sind, sodass der Typparameter an keiner Stelle in der Deklaration eines Feldes einer generischen Schnittstelle verwendet werden kann.\u00a0 Ebenso wird die verschachtelte Klasse als statische verschachtelte Klasse und nicht als innere Klasse betrachtet. Aus diesem Grund ist die Verwendung des Typparameters an einer beliebigen Stelle in der verschachtelten Klasse illegal.<\/p>\n<p><b> <i> Problemumgehung f\u00fcr verschachtelte statische Klassen und Schnittstellen:<\/i> <\/b><\/p>\n<p>Bei statisch verschachtelten Klassen und Schnittstellen ist dies kein gro\u00dfes Problem.\u00a0 Als Workaround k\u00f6nnen wir die statische Klasse oder Schnittstelle selbst generieren.<\/p>\n<p>Beispiel (Problemumgehung \u2013 Generieren Sie den verschachtelten statischen Typ):<\/p>\n<blockquote><p><tt> Klasse Wrapper<b> <span style=\"color:#000099;\"> &lt;T&gt;<\/span> <\/b> {<\/tt><br \/>\n<tt> \u00a0 Privates Finale <b> <span style=\"color:#000099;\"> T<\/span> <\/b> dasObjekt;<\/tt><\/p><\/blockquote>\n<blockquote><p><tt> \u00a0 \u00f6ffentlicher Wrapper(<b> <span style=\"color:#000099;\"> T<\/span> <\/b> t) { theObject = t; }<\/tt><br \/>\n<tt> \u00a0 \u00f6ffentlich <b> <span style=\"color:#000099;\"> T<\/span> <\/b> getWrapper() { return theObject; }<\/tt><tt> \u00a0 \u00f6ffentlich Unver\u00e4nderlich<b> &lt;T&gt;<\/b> makeImmutable() {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 neues Unver\u00e4nderliches zur\u00fcckgeben<b> &lt;T&gt;<\/b> (dasObjekt);<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 \u00f6ffentlich Ver\u00e4nderbar<b> &lt;T&gt;<\/b> makeMutable() {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 neues Ver\u00e4nderliches zur\u00fcckgeben<b> &lt;T&gt;<\/b> (dasObjekt);<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 private static &lt;A&gt; A makeClone(A theObject) { ... }<\/tt><\/p>\n<p><tt> \u00a0 \u00f6ffentliche statische Endklasse unver\u00e4nderlich<b> &lt;A&gt;<\/b> { <b> \/\/ ist jetzt eine generische Klasse<\/b> <\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Privates Finale <b> A<\/b> dasObjekt;<\/tt><\/p>\n<p><tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00f6ffentlich unver\u00e4nderlich(<b> A<\/b> arg) {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 theObject = makeClone(arg);<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00f6ffentlich <b> A<\/b> getWrappedItem() {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return makeClone(theObject);<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 \u00f6ffentliche statische Klasse Ver\u00e4nderbar<b> &lt;A&gt;<\/b> {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ... \u00e4hnlich ...<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> }<\/tt><\/p><\/blockquote>\n<p>F\u00fcr verschachtelte Aufz\u00e4hlungstypen gibt es keine solche Problemumgehung, da sie nicht generisch sein k\u00f6nnen.<\/p>\n<p><b> <i> Problemumgehung f\u00fcr verschachtelte statische Klassen:<\/i> <\/b><\/p>\n<p>Wenn der verschachtelte statische Typ eine Klasse ist, besteht eine alternative Problemumgehung darin, die statische Klasse in eine nicht statische innere Klasse umzuwandeln.<\/p>\n<p>Beispiel (Problemumgehung \u2013 verwenden Sie eine innere Klasse):<\/p>\n<blockquote><p><tt> Klasse Wrapper<b> <span style=\"color:#000099;\"> &lt;T&gt;<\/span> <\/b> {<\/tt><br \/>\n<tt> \u00a0 Privates Finale <b> <span style=\"color:#000099;\"> T<\/span> <\/b> dasObjekt;<\/tt><\/p><\/blockquote>\n<blockquote><p><tt> \u00a0 \u00f6ffentlicher Wrapper(<b> <span style=\"color:#000099;\"> T<\/span> <\/b> t) { theObject = t; }<\/tt><br \/>\n<tt> \u00a0 \u00f6ffentlich <b> <span style=\"color:#000099;\"> T<\/span> <\/b> getWrapper() { return theObject; }<\/tt><tt> \u00a0 public Mutable makeMutable() {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0 zur\u00fcckkehren <b> Das.<\/b> new Mutable(theObject);<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 public Immutable makeImmutable() {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0 zur\u00fcckkehren <b> Das.<\/b> new Immutable(theObject);<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 private static &lt;A&gt; A makeClone(A theObject) { ... }<\/tt><\/p>\n<p><tt> \u00a0 \u00f6ffentliche Abschlussklasse Immutable {\u00a0 <b> \/\/ ist keine statische Klasse mehr<\/b> <\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0 Privates Finale <b> T<\/b> dasObjekt;<\/tt><\/p>\n<p><tt> \u00a0\u00a0\u00a0\u00a0\u00a0 \u00f6ffentlich unver\u00e4nderlich(<b> T<\/b> arg) {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 theObject = makeClone(arg);<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0 }<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0 \u00f6ffentlich <b> T<\/b> getWrappedItem() {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return makeClone(theObject);<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0 }<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 \u00f6ffentliche Klasse Ver\u00e4nderbar\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ... \u00e4hnlich ...<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> }<\/tt><\/p><\/blockquote>\n<p>Diese Problemumgehung bringt einen gewissen Mehraufwand mit sich, da alle inneren Klassen einen versteckten Verweis auf eine Instanz des \u00e4u\u00dferen Typs haben, den sie in diesem Beispiel weder ben\u00f6tigen noch nutzen; Der versteckte Hinweis ist nur Gep\u00e4ck.\u00a0 H\u00e4ufig werden innere Klassen mit Schnittstellen kombiniert, um die innere Klasse als privates Implementierungsdetail der umschlie\u00dfenden Klasse zu halten.\u00a0 Das k\u00f6nnen wir hier auch tun.<\/p>\n<p>Beispiel (die vorherige Problemumgehung wurde verfeinert):<\/p>\n<blockquote><p><tt> Klasse Wrapper<b> <span style=\"color:#000099;\"> &lt;T&gt;<\/span> <\/b> {<\/tt><br \/>\n<tt> \u00a0 Privates Finale <b> <span style=\"color:#000099;\"> T<\/span> <\/b> dasObjekt;<\/tt><\/p><\/blockquote>\n<blockquote><p><tt> \u00a0 \u00f6ffentlicher Wrapper(<b> <span style=\"color:#000099;\"> T<\/span> <\/b> t) { theObject = t; }<\/tt><br \/>\n<tt> \u00a0 \u00f6ffentlich <b> <span style=\"color:#000099;\"> T<\/span> <\/b> getWrapper() { return theObject; }<\/tt><tt> \u00a0 \u00f6ffentlich Ver\u00e4nderbar<b> &lt;T&gt;<\/b> makeMutable() {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0 return this.new Mutable(theObject);<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 \u00f6ffentlich Unver\u00e4nderlich<b> &lt;T&gt;<\/b> makeImmutable() {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0 return this.new Immutable(theObject);<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 private static &lt;A&gt; A makeClone(A theObject) { ... }<\/tt><\/p>\n<p><tt> \u00a0 <b> \u00f6ffentliche Schnittstelle <\/b> Unver\u00e4nderlich&lt;S&gt; {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0 S getWrappedItem();<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 <b> \u00f6ffentliche Schnittstelle <\/b> Mutable&lt;S&gt; {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0 S getWrappedItem();<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0 void setWrappedItem(S arg);<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 <b> Privat<\/b> letzte Klasse ImmutableImplementation <b> implementiert Immutable&lt;T&gt;<\/b> {\u00a0<\/tt><\/p>\n<p><tt> \u00a0\u00a0\u00a0\u00a0\u00a0 private final T theObject;<\/tt><\/p>\n<p><tt> \u00a0\u00a0\u00a0\u00a0\u00a0 public ImmutableImplementation(T arg) {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 theObject = makeClone(arg);<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0 }<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0 public T getWrappedItem() {<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return makeClone(theObject);<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0 }<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> \u00a0 <b> Privat<\/b> Klasse MutableImplementation <b> implementiert Mutable&lt;T&gt;<\/b> {\u00a0<\/tt><br \/>\n<tt> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ... \u00e4hnlich ...<\/tt><br \/>\n<tt> \u00a0 }<\/tt><br \/>\n<tt> }<\/tt><\/p><\/blockquote>\n<p>Wie Sie sehen, m\u00fcssen die verschachtelten \u00f6ffentlichen Schnittstellen generisch sein, w\u00e4hrend die inneren privaten Klassen den Typparameter der umschlie\u00dfenden Klasse verwenden k\u00f6nnen.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<table width=\"100%\" cellpadding=\"10\">\n<tbody>\n<tr valign=\"TOP\">\n<td colspan=\"2\" width=\"68%\"><b>\u00a0<\/b><\/td>\n<\/tr>\n<tr>\n<td colspan=\"2\"><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Weitere Informationen finden Sie in dieser FAQ: <a href=\"http:\/\/www.angelikalanger.com\/GenericsFAQ\/JavaGenericsFAQ.html#SECTION1\" rel=\"nofollow\">http:\/\/www.angelikalanger.com\/GenericsFAQ\/JavaGenericsFAQ.html#SECTION1<\/a> Hier in Kurzform einige wichtige Punkte aus dieser FAQ. Beispiel (eines generischen Typs): Klassenpaar&lt;X,Y&gt; {\u00a0 \u00a0 privates X zuerst; \u00a0 privat Y Zweiter; \u00a0 \u00f6ffentliches Paar(X a1, Y a2) { \u00a0\u00a0\u00a0 zuerst = a1; \u00a0\u00a0\u00a0 Zweiter = a2; \u00a0 } \u00a0 public X getFirst() { return first; [&hellip;]<\/p>\n","protected":false},"author":65283547,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_coblocks_attr":"","_coblocks_dimensions":"","_coblocks_responsive_height":"","_coblocks_accordion_ie_support":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_wpcom_ai_launchpad_first_post":false,"_jetpack_hide_featured_image":false,"_jetpack_featured_image_duplicate_dismissed":0,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"Generics in short form","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[1017],"tags":[18369,2549795,6280762],"class_list":["post-31","post","type-post","status-publish","format-standard","hentry","category-java","tag-generics","tag-java-2","tag-java-generics"],"jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p4CoHV-v","jetpack-related-posts":[],"jetpack_likes_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/vkrastev1blog.com\/wp-json\/wp\/v2\/posts\/31","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vkrastev1blog.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vkrastev1blog.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vkrastev1blog.com\/wp-json\/wp\/v2\/users\/65283547"}],"replies":[{"embeddable":true,"href":"https:\/\/vkrastev1blog.com\/wp-json\/wp\/v2\/comments?post=31"}],"version-history":[{"count":8,"href":"https:\/\/vkrastev1blog.com\/wp-json\/wp\/v2\/posts\/31\/revisions"}],"predecessor-version":[{"id":42,"href":"https:\/\/vkrastev1blog.com\/wp-json\/wp\/v2\/posts\/31\/revisions\/42"}],"wp:attachment":[{"href":"https:\/\/vkrastev1blog.com\/wp-json\/wp\/v2\/media?parent=31"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vkrastev1blog.com\/wp-json\/wp\/v2\/categories?post=31"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vkrastev1blog.com\/wp-json\/wp\/v2\/tags?post=31"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}