3.10 Annotationen und Generics
Bis zu diesen Punkt haben wir und mit den Grundlagen der Objektorientierung beschäftigt und wissen, wie Objekte aufgebaut und Eigenschaften genutzt werden.
Mit zwei weiteren Eigenschaften der Programmiersprache wollen wir uns kurz beschäftigen: Annotationen und Generics. Beiden Punkten nähern wir uns aus der Nutzerperspektive und nicht aus der Sicht eines API-Designers, der neue Annotationen zur Verfügung stellen muss oder neue Methoden und Klassen deklariert und diese generisch parametrisierbar ausstatten möchte.
3.10.1 Generics

Java ist eine typisierte Programmiersprache, was beutetet, dass jede Variable und jeder Ausdruck einen Typ hat, den der Compiler kennt und der sich zur Laufzeit nicht ändert. Eine Zählvariable ist zum Beispiel vom Typ int, ein Abstand zwischen zwei Punkten ist vom Typ double, und ein Koordinatenpaar ist vom Typ Point. Allerdings gibt es bei der Typisierung Lücken. Nehmen wir etwa eine Liste von Punkten:
List list;
Zwar ist die Variable list nun mit List typisiert, und das ist besser als nichts, jedoch bleibt unklar, was die Liste eigentlich genau für Objekte speichert. Sind es Punkte, Personen oder rostige Fähren? Es wäre sinnvoll, nicht nur die Liste selbst als Typ zu haben, sondern sozusagen rekursiv in die Liste reinzugehen und genauen hinzuschauen, was die Liste eigentlich referenziert. Genau das ist die Aufgabe von Generics. Eine Liste erwartet seit Java 5 eine Typangabe, was sie genau speichert. Dieser Typ erscheint in spitzen Klammern hinter dem eigentlichen »Haupttyp«.
List<Point> list;
Mit Generics haben API-Designer ein Werkzeug, um Typen noch genauer vorzuschreiben. Die Entwickler des Typs List können so vom Nutzer fordern, den Elementtyp anzugeben. So können Entwickler dem Compiler genauer sagen, was sie für Typen verwenden, und es dem Compiler ermöglichen, genauere Tests zu machen. Es ist erlaubt und möglich, diesen »Nebentyp« nicht anzugeben, doch das führt zu einer Compiler-Warnung und ist nicht empfehlenswert: Je genauer Typangaben sind, desto besser ist das für alle.
Vereinzelt kommen in den nächsten Kapiteln generische Typen vor, etwa Comparable (hilft Objekte zu vergleichen). An dieser Stelle reicht es zu verstehen, dass wir als Nutzer einen Typ in spitze Klammen eintragen müssen. Mit Generics selbst beschäftigen wir uns in Kapitel 9 genauer.
3.10.2 Annotationen

In diesem Kapitel haben wir schon unterschiedliche Modifizierer kennengelernt. Darunter waren zum Beispiel static oder public. Das Besondere an diesen Modifizierern ist, dass sie die Programmsteuerung nicht beeinflussen, aber dennoch wichtige Zusatzinformationen darstellen, also Semantik einbringen. Diese Informationen nennen sich Metadaten. Die Modifizierer static, public sind Metadaten für den Compiler, doch mit etwas Fantasie lassen sich auch Metadaten vorstellen, die nicht vom Compiler, sondern von einer Java-Bibliothek ausgewertet werden. So wie public zum Beispiel dem Compiler sagt, dass ein Element für jeden sichtbar ist, kann auch auf der anderen Seite zum Beispiel ein besonderes Metadatum an einem Element hängen, um auszudrücken, dass es nur bestimmte Wertebereiche annehmen kann.
3.10.3 Eigene Metadaten setzen

Seit Java 5 gibt es eine in die Programmiersprache eingebaute Fähigkeit für Metadaten: Annotationen. Die Annotationen lassen sich wie benutzerdefinierte Modifizierer erklären. Wir können zwar keine neue Sichtbarkeit erfinden, aber dennoch dem Compiler, bestimmten Werkzeugen oder der Laufzeitumgebung durch die Annotationen Zusatzinformationen geben. Dazu ein paar Beispiele für Annotationen und Anwendungsfälle.
Annotation | Erklärung |
@WebService class Calculator { @WebMethod int add( int x, int y ) ... | Definiert einen Web-Service mit einer Web-Service-Methode. |
@Override public String toString() ... | Überschreibt eine Methode der Oberklasse. |
@XmlRoot class Person { ... | Ermöglicht die Abbildung eines Objekts auf eine XML-Datei. |
Die Tabelle soll lediglich einen Überblick geben; genaue Anwendungen und Beispiele folgen.
Annotationen werden wie zusätzliche Modifizierer gebraucht, doch unterscheiden sie sich durch ein vorangestelltes @-Zeichen (das @-Zeichen, AT, ist auch eine gute Abkürzung für Annotation Type). Daher ist auch die Reihenfolge egal, sodass es zum Beispiel
- @Override public String toString() oder
- public @Override String toString()
lauten kann. Es ist aber üblich, die Annotationen an den Anfang zu setzen.
3.10.4 Annotationstypen @Override, @Deprecated, @SuppressWarnings

Das Paket java.lang deklariert vier Annotationstypen (einer davon ist neu in Java 7), wobei uns @Override ab Kapitel 5, »Eigene Klassen schreiben«, noch häufiger über den Weg laufen wird.
Die vier Annotationen haben vom Compiler beziehungsweise Laufzeitsystem eine besondere Semantik. Java SE deklariert in anderen Paketen (wie dem javax.annotation-Paket) noch weitere allgemeine Annotationstypen, doch die sind an dieser Stelle nicht relevant. Dazu kommen spezielle technologiespezifische Annotationstypen wie für die XML-Objekt-Abbildung oder Web-Service-Deklarationen.
Die Begriffe »Annotation« und »Annotationstyp«
Die Annotationstypen sind die Deklarationen, wie etwa ein Klassentyp. Werden sie an ein Element gehängt, ist es eine konkrete Annotation. Während also Override selbst der Annotationstyp ist, ist @Override vor toString() die konkrete Annotation.
@Deprecated
Die Annotation @Deprecated übernimmt die gleiche Aufgabe wie das JavaDoc-Tag @deprecated: Die markierten Elemente werden als veraltet markiert und drücken damit aus, dass der Entwickler Alternativen nutzen soll.
Beispiel |
Die Methode fubar()[110](Im US-Militär-Slang steht das für: »Fucked up beyond any recognition« – »vollkommen ruiniert«.) soll als veraltet markiert werden: @Deprecated |
Die Übersetzung mit dem Schalter -Xlint:deprecation liefert die genauen Warnungen; im Moment ist das mit -deprecation gleich.
Auch über ein JavaDoc-Tag kann ein Element als veraltet markiert werden. Ein Unterschied bleibt: Das JavaDoc-Tag kann nur von JavaDoc (beziehungsweise einem anderen Doclet) ausgewertet werden, während Annotationen auch andere Tools auswerten können.
Annotationen mit zusätzlichen Informationen
Die Annotationen @Override und @Deprecated gehören zur Klasse der Marker-Annotationen, weil keine zusätzlichen Angaben nötig (und erlaubt) sind. Zusätzlich gibt es die Single-Value Annotation, die genau eine zusätzliche Information bekommt, und eine volle Annotation mit beliebigen Schlüssel/Werte-Paaren.
Schreibweise der Annotation | Funktion |
@Annotationstyp | (Marker-)Annotation |
@Annotationstyp( Wert ) | Annotation mit genau einem Wert |
@Annotationstyp( Schlüssel1=Wert1,
|
Annotation mit Schlüssel/Werte-Paaren |
Klammern sind bei einer Marker-Annotation optional.
@SuppressWarnings
Die Annotation @SuppressWarnings steuert Compiler-Warnungen. Unterschiedliche Werte bestimmen genauer, welche Hinweise unterdrückt werden. Nützlich ist die Annotation bei der Umstellung von Quellcode, der vor Java 5 entwickelt wurde. Mit Java 5 zogen Generics ein, eine Möglichkeit, dem Compiler noch mehr Informationen über Typen zu geben. Die Java API-Designer haben daraufhin die Deklaration der Datenstrukturen überarbeitet und Generics eingeführt, was dazu führt, dass vor Java 5 entwickelter Quellcode mit einem Java 5-Compiler eine Vielzahl von Warnungen ausgibt. Nehmen wir folgenden Programmcode:
ArrayList list;
list = new ArrayList();
list.add( "SuppressWarnings" );
Eclipse zeigt die Meldungen direkt an, NetBeans dagegen standardmäßig nicht.


Abbildung 3.10: Warnungen in Eclipse
Der Compiler javac meldet über die Kommandozeile recht unspezifisch:
Note: ABC.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
Mit dem gesetzten Schalter -Xlint heißt es dann genauer:
warning: [rawtypes] found raw type: ArrayList
ArrayList list1;
^
missing type arguments for generic class ArrayList<E>
where E is a type-variable:
E extends Object declared in class ArrayList
warning: [rawtypes] found raw type: ArrayList
list1 = new ArrayList();
^
missing type arguments for generic class ArrayList<E>
where E is a type-variable:
E extends Object declared in class ArrayList
warning: [unchecked] unchecked call to add(E) as a member of the raw type ArrayList
list1.add("SuppressWarnings");
^
where E is a type-variable:
E extends Object declared in class ArrayList
Zwei unterschiedliche Arten von Warnungen treten auf:
- Da die Klasse ArrayList als generischer Typ deklariert ist, melden die ersten beiden Zeilen »found raw type: ArrayList« (javac) bzw. »ArrayList is a raw type. References to generic type ArrayList<E> should be parameterized« (Eclipse).
- Die dritte Zeile nutzt mit add() eine Methode, die über Generics einen genaueren Typparameter bekommen könnte. Da wir einen Typ aber nicht angegeben haben, folgt die Warnung: »unchecked call to add(E) as a member of the raw type ArrayList« (javac) bzw. »Type safety: The method add(Object) belongs to the raw type ArrayList. References to generic type ArrayList<E> should be parameterized« (Eclipse).
Warnungen lassen sich über die Annotation @SuppressWarnings ausschalten. Als spezieller Modifizierer lässt sich die Annotation an der Variablendeklaration anbringen, an der Methodendeklaration oder an der Klassendeklaration. Die Reichweite ist aufsteigend. Wer bei altem Programmcode kurz und schmerzlos alle Warnungen abschalten möchte, der setzt ein @SuppressWarnings("all") an die Klassendeklaration.
Beispiel |
Der Compiler soll keine Meldungen für die Klasse geben: @SuppressWarnings( "all" ) |
Anstatt jede Warnung zu unterdrücken, ist es eine bessere Strategie, selektiv vorzugehen. Eclipse unterstützt uns mit einem Quick-Fix und schlägt für unser Beispiel Folgendes vor:
- @SuppressWarnings("rawtypes") für ArrayList list und list = new ArrayList()
- @SuppressWarnings("unchecked") für list.add("...")
Da zwei gleiche Modifizierer nicht erlaubt sind – und auch zweimal @SuppressWarnings nicht –, wird eine besondere Array-Schreibweise gewählt.
Beispiel |
Der Compiler soll für die ungenerisch verwendete Liste und deren Methoden keine Meldungen geben: @SuppressWarnings( { "rawtypes", "unchecked" } ) |
Kurz kam bereits zur Sprache, dass die @SuppressWarnings-Annotation auch an der Variablendeklaration möglich ist. Für unser Beispiel hilft das allerdings wenig, wenn etwa bei der Deklaration der Liste alle Warnungen abgeschaltet werden:
@SuppressWarnings( "all" ) ArrayList list;
list = new ArrayList(); // Warnung: ArrayList is a raw type...
list.add( "SuppressWarnings" ); // Warnung: Type safety ...
Das @SuppressWarnings("all") gilt nur für die eine Deklaration ArrayList list und nicht für folgende Anweisungen, die etwas mit der list machen. Zur Verdeutlichung setzt das Beispiel die Annotation daher in die gleiche Zeile.
Hinweis |
Die Schreibweise @SuppressWarnings("xyz") ist nur eine Abkürzung von @SuppressWarnings({"xzy"}), und das wiederum ist nur eine Abkürzung von @SuppressWarnings(value= {"xzy"}). |
Ihr Kommentar
Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.