Was bedeuten die Ausdrücke ? super T, ? extends T und ? in Java?
Bei dem hier genannten ‚?’ handelt es sich um sogenannte Wildcards. Diese Wildcard repräsentieren einen unbekannten Typ, daher kann man es auch mit einem Platzhalter vergleichen. Im Gegensatz zu einem Typ-Parameter wie ‚<T>’, können Wildcards nicht als Platzhalter für Typ-Parameter weiter genutzt werden. Wildcards sind ein Laufzeit-Konstrukt, während Generic-Types (‚T‘) vom Compiler bereits geprüft werden können. Mittels Wildcards verallgemeinert man sozusagen den zu verwendeten Typ, während man mit ‚<T>’ sich explizit auf den übergebenen Typ festlegt.
Java Wildcard-Arten
Es gibt drei verschiedenen Möglichkeiten ein Wildcard zu definieren: „Lower Bound“, „Upper Bound“ und „Unbounded“.
Upper Bound
Mittels „extends“ definiert man einen „Upper Bound“. Dieser wird genutzt, wenn man beispielsweise eine Collection von sämtlichen Zahlentypen verwenden möchte. Demnach kann man den Diamant-Operator ‚? extends Number’ schreiben. Dies macht deutlich, dass die folgende Collection nicht nur Integer-Typen aufnehmen kann, sondern auch Objekte vom Typ Double usw.
List<? extends Number> myList = null; myList = new new ArrayList<Integer>(); myList = new new ArrayList<Double>(); myList = new new ArrayList<Number>(); myList = new new ArrayList<String>(); //ungültig!!
Der letzte Ausdruck ist ungültig, da myList nur Listen vom Untertyp Number akzeptiert.
Lower Bound
Mittels „super“ definiert man einen „Lower Bound“. Damit kann man beispielsweise ‚? super A’ schreiben, um die erlaubten Typen auf alle Untertypen von A (A mit eingeschlossen) zu beschränken. So kann man beispielsweise folgendes Konstrukt verwenden, um die erlaubten Typen auf ausschließlich RuntimeExceptions und Unterklassen von RuntimeExceptions zu beschränken
List<? super RuntimeException> myList = new ArrayList<>(); myList.add(new RuntimeException()); myList.add(new NullPointerException()); myList.add(new IOException()); //ungültig!!
Letzteres führt zu einem Fehler, da IOException nicht vom Typ RuntimeException erbt, sondern vom Typ Exception. RuntimeException erbt zwar auch von Exception, aber durch die obige Deklaration haben wir ausschließlich Typen erlaubt, die von RuntimeException erben.
Durch die oben genannten Ausdrücke kann man so einen Gültigkeitsbereich von erlaubten Typen für generische Klassen (Generics) oder Methoden deklarieren, um beispielsweise eine Liste für Nummern vor einem „Missbrauch“ mit Strings zu bewahren. (Siehe Beispiel oben)
Grob gesagt: Der Unterschied zwischen ‚super’ und ‚extends’ besteht darin, in welcher Ableitungs-Hierarchie ein Typ stehen darf.
Hallo DerPade,
deine Erklärung zu Lower Bound ist leider nicht korrekt. Was du hier beschreibst ist die Upper Bound. Bei der Lower Bound sind nicht Untertypen von A erlaubt sondern nur A und alle Supertypen davon. Für dein Bespiel bedeutet das nur RuntimeException und alle Supertypen davon (also Exception und Throwable).
Und ja IOException ist in deinem Beispiel nicht möglich. Aber auch hier ist die Erklärung nicht korrekt. Die IOException ist zwar eine Exception aber eben kein Supertyp von RuntimeException. Würde die IOException von RuntimeException erben, wäre sie dennoch nicht erlaubt, da eben nur Supertypen von RuntimeException erlaubt sind.