Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger. 
Inhaltsverzeichnis
Vorwort
1 Java ist auch eine Sprache
2 Imperative Sprachkonzepte
3 Klassen und Objekte
4 Der Umgang mit Zeichenketten
5 Eigene Klassen schreiben
6 Objektorientierte Beziehungsfragen
7 Ausnahmen müssen sein
8 Äußere.innere Klassen
9 Besondere Typen der Java SE
10 Generics<T>
11 Lambda-Ausdrücke und funktionale Programmierung
12 Architektur, Design und angewandte Objektorientierung
13 Komponenten, JavaBeans und Module
14 Die Klassenbibliothek
15 Einführung in die nebenläufige Programmierung
16 Einführung in Datenstrukturen und Algorithmen
17 Einführung in grafische Oberflächen
18 Einführung in Dateien und Datenströme
19 Einführung ins Datenbankmanagement mit JDBC
20 Einführung in <XML>
21 Testen mit JUnit
22 Bits und Bytes und Mathematisches
23 Die Werkzeuge des JDK
A Java SE-Paketübersicht
Stichwortverzeichnis


Download:

- Beispielprogramme, ca. 35,4 MB


Buch bestellen
Ihre Meinung?



Spacer
<< zurück
Java ist auch eine Insel von Christian Ullenboom

Einführung, Ausbildung, Praxis
Buch: Java ist auch eine Insel


Java ist auch eine Insel

Pfeil 10 Generics<T>
Pfeil 10.1 Einführung in Java Generics
Pfeil 10.1.1 Mensch versus Maschine – Typprüfung des Compilers und der Laufzeitumgebung
Pfeil 10.1.2 Taschen
Pfeil 10.1.3 Generische Typen deklarieren
Pfeil 10.1.4 Generics nutzen
Pfeil 10.1.5 Diamonds are forever
Pfeil 10.1.6 Generische Schnittstellen
Pfeil 10.1.7 Generische Methoden/Konstruktoren und Typ-Inferenz
Pfeil 10.2 Umsetzen der Generics, Typlöschung und Raw-Types
Pfeil 10.2.1 Realisierungsmöglichkeiten
Pfeil 10.2.2 Typlöschung (Type Erasure)
Pfeil 10.2.3 Probleme der Typlöschung
Pfeil 10.2.4 Raw-Type
Pfeil 10.3 Einschränken der Typen über Bounds
Pfeil 10.3.1 Einfache Einschränkungen mit extends
Pfeil 10.3.2 Weitere Obertypen mit &
Pfeil 10.4 Typparameter in der throws-Klausel *
Pfeil 10.4.1 Deklaration einer Klasse mit Typvariable <E extends Exception>
Pfeil 10.4.2 Parametrisierter Typ bei Typvariable <E extends Exception>
Pfeil 10.5 Generics und Vererbung, Invarianz
Pfeil 10.5.1 Arrays sind kovariant
Pfeil 10.5.2 Generics sind nicht kovariant, sondern invariant
Pfeil 10.5.3 Wildcards mit ?
Pfeil 10.5.4 Bounded Wildcards
Pfeil 10.5.5 Bounded-Wildcard-Typen und Bounded-Typvariablen
Pfeil 10.5.6 Das LESS-Prinzip
Pfeil 10.5.7 Enum<E extends Enum<E>> *
Pfeil 10.6 Konsequenzen der Typlöschung: Typ-Token, Arrays und Brücken *
Pfeil 10.6.1 Typ-Token
Pfeil 10.6.2 Super-Type-Token
Pfeil 10.6.3 Generics und Arrays
Pfeil 10.6.4 Brückenmethoden
Pfeil 10.6.5 Zum Weiterlesen
 

Zum Seitenanfang

10.6Konsequenzen der Typlöschung: Typ-Token, Arrays und Brücken * Zur vorigen ÜberschriftZur nächsten Überschrift

Die Typlöschung ist im Allgemeinen kein so großes Problem, doch in speziellen Situationen ist es lästig, dass der Typ nicht zur Laufzeit vorliegt.

 

Zum Seitenanfang

10.6.1Typ-Token Zur vorigen ÜberschriftZur nächsten Überschrift

Wir haben zum Beispiel gesehen, dass, wenn eine Tasche mit der Typvariablen T deklariert wurde, dieses T nicht wirklich wie ein Makro durch den Typparameter ersetzt wird, sondern dass in der Regel nur einfach Object eingesetzt wird:

class Pocket<T> {

T newPocketContent() { return new T(); } // inline Compilerfehler

}

Aus new T() macht die Typlöschung also new Object(), und das ist nichts wert. Doch wie kann dennoch ein Typ erzeugt werden und der Typ T zur Laufzeit vorliegen?

Hier lässt sich ein Trick nutzen, nämlich ein Class-Objekt für den Typ einzusetzen.

Typparameter

Class-Objekt repräsentiert Typparameter

String

String.class

Integer

Integer.class

Tabelle 10.12Transfer der Typparameter durch Class-Objekte

Dieses Class-Objekt, das nun den Typ repräsentiert, heißt Typ-Token (engl. type token). Es kommt uns natürlich entgegen, dass Class selbst als generischer Typ deklariert ist und zwei interessante Methoden ebenfalls »generifiziert« wurden:

Class<String> clazz1 = String.class;

String newInstance = clazz1.newInstance();

Class< ? extends String> clazz2 = newInstance.getClass();

System.out.println( clazz1.equals( clazz2 ) ); // true

Zunächst ist da die Methode newInstance(). Sie erzeugt ein neues Exemplar mit dem Typ, den das Class-Objekt repräsentiert:

final class java.lang.Class<T>

implements Serializable, GenericDeclaration, Type, AnnotatedElement
  • public T newInstance()

      throws InstantiationException, IllegalAccessException

Mit einem gegebenen Objekt lässt sich mit getClass() das zugehörige Class-Objekt zur Klasse erfragen:

class java.lang.Object
  • public final native Class<?> getClass()

    Liefert Class-Objekt.

[»]Hinweis

Die Rückgabe Class<?> bei getClass() ist unschön, insbesondere die allgemeine Wildcard. Sie verhindert, dass sich Folgendes schreiben lässt:

Class<String> clazz = "ARTE".getClass(); // inline Compilerfehler »Type mismatch«

Stattdessen muss es so heißen:

Class<? extends String> clazz = "ARTE".getClass();

Da Object nicht generisch deklariert ist, ist es kein Wunder, dass getClass() keine genaueren Angaben machen kann.

Lösungen mit dem Typ-Token

Um das Typ-Token einzusetzen, muss das Class-Objekt mit als Argument in einem Konstruktor oder einer Methode übergeben werden. So lässt sich etwa eine newInstance()-Methode nachbauen, die die geprüften Exceptions fängt und im Fehlerfall als RuntimeException meldet. Gut zu sehen ist, wie sich der Typ des Class-Objekts auf die Rückgabe überträgt:

public static <T> T newInstance( Class<T> type ) {

try {

return type.newInstance();

}

catch ( ReflectiveOperationException e ) {

throw new RuntimeException( e );

}

}
 

Zum Seitenanfang

10.6.2Super-Type-Token Zur vorigen ÜberschriftZur nächsten Überschrift

Mit einem Class-Objekt lässt sich gut ein Typ repräsentieren, allerdings gibt es ein Problem. Das Class-Objekt kann selbst keine generischen Typen darstellen:

Typparameter

Class-Objekt repräsentiert Typparameter

String

String.class

Integer

Integer.class

Pocket<String>

Pocket<String>.class Geht nicht! inline

Tabelle 10.13Ein Class-Objekt kann keinen generischen Typ beschreiben.

Der wirkliche Typ lässt sich nur mit viel Getrickse bestimmen und festhalten. Hier kommt die Reflection-API zum Einsatz, sodass nur kurz die Klasse und ein Beispiel vorgestellt werden sollen. Hier die Klasse:

Listing 10.25com/tutego/insel/generic/TypeRef, TypeRef

public abstract class TypeRef<T> {



public final Type type;



protected TypeRef() {

ParameterizedType superclass = (ParameterizedType) getClass().getGenericSuperclass();

type = superclass.getActualTypeArguments()[0];

}

}

Und ein Beispiel, das eine anonyme Unterklasse erzeugt und so den Typ zugänglich macht:

Listing 10.26com/tutego/insel/generic/TypeRefDemo, main()

TypeRef<Pocket<String>> ref1 = new TypeRef<Pocket<String>>(){};

System.out.println( ref1.type ); // com.tutego.insel.generic.Pocket<java.lang.String>

TypeRef<Pocket<Byte>> ref2 = new TypeRef<Pocket<Byte>>(){};

System.out.println( ref2.type ); // com.tutego.insel.generic.Pocket<java.lang.Byte>

Damit konnten wir den Typparameter über java.lang.reflect.Type festhalten, und ref1 unterscheidet sich eindeutig von ref2. Der Typ liegt jedoch nicht als Class-Objekt vor, und Operationen wie newInstance() sind auf Type nicht möglich – die Schnittstelle deklariert überhaupt keine Methoden, sondern repräsentiert nur Typen.

 

Zum Seitenanfang

10.6.3Generics und Arrays Zur vorigen ÜberschriftZur nächsten Überschrift

Die Typlöschung ist der Grund dafür, dass Arrays nicht so umgesetzt werden können, wie es sich der Entwickler denkt.[ 205 ](Bei Oracle (http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4888066) ist dafür ein Bug gelistet. Suns Antwort auf die Bitte, ihn zu beheben, lautet lapidar: »Some day, perhaps, but not now.« ) Folgendes ergibt einen Compilerfehler:

class TwoBox<T> {

T[] array = new T[ 2 ]; // inline Cannot create a generic array of T

T[] getArray() { return array; }

}

Der Grund für diesen Fehler ist dann gut zu erkennen, wenn wir überlegen, zu welchem Programmcode die Typlöschung führen würde:

class TwoBox {

Object[] array = new Object[ 2 ]; // (1)

Object[] getArray() { return array; }

}

Der Aufrufer würde nun die TwoBox parametrisiert verwenden wollen:

TwoBox<String> twoStrings = new TwoBox<String>();

String[] stringArray = twoStrings.getArray();

Denken wir an dieser Stelle wieder an die Typlöschung und an das, was der Compiler generiert:

TwoBox twoStrings = new TwoBox();

String[] stringArray = (String[]) twoStrings.getArray(); // (2)

Jetzt ist es auffällig: Während (1) ein Object-Array der Länge 2 aufbaut und auch getArray() dies als Object-Array nach außen gibt, castet (2) dieses Object-Array auf ein String-Array. Das geht aber nicht, denn diese beiden Typen sind nicht typkompatibel. Zwar kann natürlich ein Object-Array Strings referenzieren, aber das Array selbst als Objekt ist eben ein Object[] und kein String[].

Reflection hilft

Die Java-API bietet über Reflection wieder eine Möglichkeit, Arrays eines Typs zu erzeugen:

T[] array = (T[]) Array.newInstance( clazz, 2 );

Allerdings muss der Class-Typ clazz bekannt sein und als zusätzlicher Parameter übergeben werden. Die Syntax T.class ergibt einen Compilerfehler, denn über die Typlöschung wäre das ja sowieso immer Object.class, was den gleichen Fehler wie vorher zur Folge hätte und kein Fortschritt wäre.

 

Zum Seitenanfang

10.6.4Brückenmethoden Zur vorigen ÜberschriftZur nächsten Überschrift

Aus der Tatsache, dass mit Generics übersetzte Klassen auf einer JVM lauffähig sein müssen, die kein generisches Typsystem besitzt, folgen diverse Hacks, die der Compiler zur Erhaltung der heiligen Kompatibilität vornimmt. Er fügt neue Methoden ein, so genannte Brückenmethoden, damit der Bytecode nach der Typlöschung auch von älteren Programmen genutzt werden kann.

Brückenmethode wegen Typvariablen in Parametern

Starten wir mit der Schnittstelle Comparable, die generisch deklariert wurde:

public interface Comparable<T> { public int compareTo( T o ); }

Die bekannte Klasse Integer implementiert zum Beispiel diese Schnittstelle und kann somit sagen, wie die Ordnung zu einem anderen Integer-Objekt ist:

Listing 10.27java/lang/Integer.java, Ausschnitt

public final class Integer extends Number implements Comparable<Integer> {



private final int value;



public Integer( int value ) { this.value = value; }



public int compareTo( Integer anotherInteger ) {

int thisVal = this.value;

int anotherVal = anotherInteger.value;

return ( thisVal < anotherVal ? –1 : (thisVal == anotherVal ? 0 : 1) );

}

...

}

Die Klasse Integer implementiert die Methode compareTo(…) mit dem Parametertyp Integer. Der Compiler wird also eine Methode mit der Signatur compareTo(Integer) erstellen. Doch damit beginnt ein Problem! Wir haben eine unbekannte Anzahl an Zeilen Quellcode, die sich auf eine Methode compareTo(Object) beziehen, denn vor Java 5 war die Signatur ja anders.

Damit es nicht zu Inkompatibilitäten kommt, setzt der Compiler einfach noch die Methode compareTo(Object) bei Integer dazu. Die Implementierung sieht so aus, dass sie einfach delegiert:

public int compareTo( Object anotherInteger ) {

return compareTo( (Integer) anotherInteger );

}

Brückenmethode wegen kovarianter Rückgabetypen

Wenn eine Methode überschrieben wird, so muss die Unterklasse die gleiche Signatur (also den gleichen Methodennamen und die gleiche Parameterliste) besitzen. Nehmen wir eine Klasse CloneableFont, die Font erweitert und die clone()-Methode aus Object überschreibt. Eine Klasse, die sich auch unter Java 1.4 übersetzen lässt, würde so aussehen:

Listing 10.28com/tutego/insel/nongeneric/CloneableFont.java, CloneableFont

public class CloneableFont extends Font implements Cloneable {



public CloneableFont( String name, int style, int size ) {

super( name, style, size );

}



@Override public Object clone() {

return new Font( getAttributes() );

}

}

Im Bytecode der Klasse CloneableFont sind somit ein Konstruktor und eine Methode vermerkt.

Dazu kurz ein Blick auf die Ausgabe des Dienstprogramms javap, das die Signaturen anzeigt:

$ javap com.tutego.insel.nongeneric.CloneableFont

Compiled from "CloneableFont.java"

public class com.tutego.insel.nongeneric.CloneableFont extends java.awt.Font{

public com.tutego.insel.nongeneric.CloneableFont(java.lang.String, int, int);

public java.lang.Object clone();

}

Nehmen wir nun an, eine zweite Klasse ist Nutzer von CloneableFont:

Listing 10.29com/tutego/insel/nongeneric/CloneableFontDemo.java, main()

CloneableFont font = new CloneableFont( "Arial", Font.BOLD, 12 );

Object font2 = font.clone();

Beim Aufruf font.clone() prüft der Compiler, ob die Methode clone() in CloneableFont aufrufbar ist, und trägt dann die exakte Signatur mit Rückgabe – das ist der entscheidende Punkt – in den Bytecode ein. Die Anweisung font.clone() sieht im Bytecode von CloneableFontDemo.class etwa so aus (disassembliert mit javap):

invokevirtual #23;

#23 ist ein Verweis auf die clone()-Methode von CloneableFont, und invokevirtual ist der Bytecodebefehl zum Aufruf der Methode. Hinter der 23 steckt eine JVM-Methodenkennung, die von javap so ausgegeben wird:

com/tutego/insel/nongeneric/CloneableFont.clone:()Ljava/lang/Object;

Im Bytecode steht exakt ein Verweis auf die Methode clone() mit dem Rückgabetyp Object.

Seit Java 5 ist eine kleine Änderung beim Überschreiben hinzugekommen. Wenn eine Unterklasse eine Methode überschreibt, kann sie den Rückgabetyp auf einen Untertyp präzisieren – das nennt sich kovariantes Überschreiben. Also kann clone() statt Object jetzt Font zurückgeben.

Gleicher Rückgabetyp wie die überschriebene Methode

Kovarianter Rückgabetyp

com/tutego/insel/nongeneric/CloneableFont.java, clone()

@Override public Object clone() {

 return new Font( getAttributes() );

}

com/tutego/insel/generic/CloneableFont.java, clone()

@Override public Font clone() {

 return new Font( getAttributes() );

}

Tabelle 10.14Beispiel kovarianter Rückgabetypen

Da der Rückgabetyp der überschriebenen Methode nun nicht mehr Object, sondern Font ist, ändert sich auch der Bytecode von CloneableFont. Die Datei CloneableFont.class ist ohne kovariante Rückgabe 593 Byte groß und mit kovarianter Rückgabe 739 Byte. (Warum dieser satte Unterschied? Dazu gleich mehr im folgenden Abschnitt.)

Stellen wir uns nun Folgendes vor: Die erste Version von CloneableFont wurde lange vor der Existenz von Generics implementiert und konnte kein kovariantes Überschreiben nutzen. Die Klasse CloneableFont ist unglaublich populär, und die Methode clone() – die Object liefert – wird von vielen Stellen aufgerufen. Im Bytecode der nutzenden Klassen gibt es also immer einen Bezug zu der Methode mit der JVM-Signatur:

com/tutego/insel/nongeneric/CloneableFont.clone:()Ljava/lang/Object;

Bei einem Refactoring geht der Autor der Klasse CloneableFont über seine Klasse und sieht, dass er bei clone() die kovariante Rückgabe nutzen kann. Er korrigiert die Methode und setzt statt Object den Typ Font ein. Er compiliert die Klasse und setzt sie wieder in die Öffentlichkeit.

Nun stellt sich die Frage, was mit den Nutzern ist, also Klassen wie CloneableFontDemo, die nicht neu übersetzt werden, denn sie suchen eine Methode mit dieser JVM-Signatur:

com/tutego/insel/nongeneric/CloneableFont.clone:()Ljava/lang/Object;

Da clone() in CloneableFont in der aktuellen Version nun Font zurückgibt, müsste die JVM einen Fehler auslösen, denn der Bytecode ist ja anders, und die gefragte Methode mit der Rückgabe Object ist nicht mehr da. Das wäre ein riesiges Problem, denn so würden durch die Änderung des Autors alle nutzenden Klassen illegal, und die Projekte mit diesen Klassen ließen sich alle nicht mehr übersetzen.

Doch es gibt keinen Anlass zu Panik. Es kommt nicht zu einem Compilerfehler, da der Compiler eine Hilfsmethode einfügt, die in der JVM-Signatur mit der Java 1.4-Variante von clone(), also mit der Rückgabe Object, übereinstimmt. Der Disassembler javap zeigt das gut:

$ javap com.tutego.insel.generic.CloneableFont

Compiled from "CloneableFont.java"

public class com.tutego.insel.generic.CloneableFont extends java.awt.Font{

public com.tutego.insel.generic.CloneableFont(java.lang.String, int, int);

public java.awt.Font clone();

public java.lang.Object clone() throws java.lang.CloneNotSupportedException;

}

Die clone()-Methode gibt es also zweimal! Interessant ist die Besonderheit, dass Dinge im Bytecode erlaubt sind, die im Java-Programmcode nicht möglich sind. In Java gehört der Rückgabetyp nicht zur Signatur, und der Java-Compiler erlaubt nicht zwei Methoden mit der gleichen Signatur. Im Bytecode allerdings gehört der Rückgabetyp schon dazu, und daher sind die Methoden dort erlaubt, da sie klar unterscheidbar sind.

Übersetzt ein aktueller Compiler die Klasse CloneableFont mit dem kovarianten Rückgabetyp bei clone(), so setzt er automatisch die Brückenmethode ein. Das erklärt auch, warum der Bytecode der Klassen mit kovarianten Rückgabetypen größer ist. So finden auch die alten Klassen, die auf die ursprüngliche clone()-Methode mit der Rückgabe Object compiliert sind, diese Methode, und es gibt keinen Laufzeitfehler.

Als Letztes muss noch geklärt werden, was der Compiler eigentlich genau für eine Brückenmethode generiert. Das ist einfach, denn in die Brückenmethode setzt der Compiler eine Weiterleitung:

@Override public Font clone() {

return new Font( getAttributes() );

}

@Override public Object clone() {

return (Font) clone(); // Vom Compiler in Bytecode generiert

}

Bleibt festzuhalten, dass auf Ebene der JVM kovariante Rückgabetypen nicht möglich sind und im Bytecode immer die Methode inklusive Rückgabetyp referenziert wird.

[»]Hinweis

In Java sind alle Methoden einer Klasse, die sich auch im Bytecode befinden, über Reflection zugänglich. Lästig wäre es nun, wenn Tools Methoden sähen, die der Compiler eingeführt hat, weil dieser herumtricksen und Beschränkungen umschiffen musste. Die Brückenmethoden werden daher mit einem speziellen Flag markiert und als synthetische Methoden (engl. synthetic method) gekennzeichnet. Das Flag lässt sich über Reflection mit isSynthetic() an den Field-Objekten erfragen.

Brückenmethode wegen einer Typvariablen in der Rückgabe

Werfen wir einen Blick auf ein ähnliches Szenario, bei dem der Rückgabetyp durch eine Typvariable einer generisch deklarierten Klasse bestimmt wird, und sehen wir uns an, welche Konsequenzen sich im Bytecode daraus ergeben.

Die Schnittstelle Iterator dient im Wesentlichen dazu, Datensammlungen nach ihren Daten zu fragen. Die beiden Spalten zeigen die Deklaration der Iterator-Schnittstelle unter Java 1.4 und Java 5:

Java 1.4

Java 5

public interface Iterator {

boolean hasNext();

Object next();

void remove();

}
public interface Iterator<E> {

boolean hasNext();

E next();

void remove();

}

Tabelle 10.15Veränderung der Implementierung der Schnittstelle Iterator

So soll hasNext() immer true ergeben, wenn der Iterator weitere Daten liefern kann, und next() liefert schlussendlich das Datum selbst. In der Variante unter Java 5 ist der Rückgabetyp von next() durch die Typvariable bestimmt.

Warum Brückenmethoden benötigt werden, zeigt wieder ein Beispiel, in dem Entwickler mit Java 1.4 begannen und später ihr Programm mit Generics verfeinern. Trotz der Änderung muss eine alte, mit Java 1.4 compilierte Version noch funktionieren.

Üblicherweise liefern Iteratoren alle Elemente einer Datenstruktur. Doch anstatt durch eine Datenstruktur zu laufen, soll unser Iterator, ein EndlessRandomIterator, unendlich viele Zufallszahlen liefern. Wir interessieren uns besonders für die Implementierung der Schnittstelle Iterator. Ohne Generics unter Java 1.4 sieht das so aus:

Listing 10.30com/tutego/insel/nongeneric/EndlessRandomIterator.java, EndlessRandomIterator

public class EndlessRandomIterator implements Iterator {

@Override public boolean hasNext() { return true; }

@Override public Object next() { return Double.valueOf( Math.random() ); }

@Override public void remove() { throw new UnsupportedOperationException(); }

}

Ein Programm, das den EndlessRandomIterator nutzt, empfängt bei next() nun ein Double. Aber durch den Ausschluss der Kovarianz in Java 1.4 kann durch die Deklaration von Object next() in Iterator in unserer Klasse EndlessRandomIterator auch nur Object next() stehen. Ein Nutzer von next() wird also auf jeden Fall die Methode next() mit dem Rückgabetyp Object erwarten und so im Bytecode vermerken – die Begründung hatten wir schon bei der Kovarianz im vorangehenden Abschnitt aufgeführt.

Passen wir den EndlessRandomIterator mit Generics an, ändern sich drei Dinge:

  1. Erstens wird implements Iterator zu implements Iterator<Double>.

  2. Dann wird Object next() zu Double next().

  3. Drittens kann es im Rumpf von next() durch das Autoboxing etwas kürzer werden, nämlich return Math.random().

Der wichtige Punkt ist, dass sich der Rückgabetyp bei next() von Object in Double ändert. An der Stelle muss der Compiler mit einer Brückenmethode eingreifen, sodass im Bytecode wieder zwei next()-Methoden stehen: Einmal Object next() und dann Double next(). Denn ohne Object next() würde der alte Programmcode, der Object next() erwartet, plötzlich nicht mehr laufen, und das wäre ein Bruch der Abwärtskompatibilität.

 

Zum Seitenanfang

10.6.5Zum Weiterlesen Zur vorigen ÜberschriftZur nächsten Überschrift

Generics sind für den Nutzer zum Glück relativ einfach zu verstehen, weil sie nur in die spitzen Klammern einen Typ setzen müssen und dann fertig sind. Bibliotheksdesigner haben es da deutlich schwerer, weil sie überlegen müssen, welche Typen tatsächlich für eine Operation nötig sind, und entsprechend Wildcards anbieten müssen. Zum Training ist es sinnvoll, sich die Anwendung der Generics aller Methoden von java.util.Collections einmal vorzunehmen.

Auf den Webseiten http://gafter.blogspot.com/2006/12/super-type-tokens.html und http://www.artima.com/weblogs/viewpost.jsp?thread=206350 sind weitere Informationen und Einsatzgebiete zu finden. Super-Type-Tokens kommen in der Java-Welt nicht besonders oft vor.

 


Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.

>> Zum Feedback-Formular
<< zurück

 

 


Copyright © Rheinwerk Verlag GmbH 2017

Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 

[Rheinwerk Computing]



Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de