javaseiten.de   |   Version 0.6
 

2.4. Vordefinierte Annotationstypen

Eine Reihe von Annotationstypen sind standardmäßig vordefiniert und für jeden dieser Typen existiert eine Klassendatei. So ist z.B. die zum vordefinierten Annotationstyp Deprecated gehörende Datei Deprecated.class im Paket java.lang enthalten. Die Meta-Annotationen @Documented, @Inherited, @Target und @Retention werden bei der Deklaration von "normalen" Annotationen eingesetzt, um gewisse Eigenschaften dieser "normalen" Annotationen festzulegen (Dokumentierbarkeit, Vererbbarkeit, Verfügbarkeit (Auslesbarkeit) während der Laufzeit und Anwendbarkeit auf verschiedene Programmelemente).

2.4.1. Deprecated

Programmelemente wie z.B. Methoden können mit @Deprecated deklariert werden, um anzuzeigen, dass diese Elemente womöglich in Folgeversionen nicht mehr verfügbar sein werden. Bei der Deklaration dieses Annotationstyps wurden die Meta-Annotationen @Documented und @Retention verwendet:

java.lang 
Annotation Type Deprecated
1.5

@Documented
@Retention(value=RUNTIME)
public @interface Deprecated

Die folgende Klasse besitzt die Mehtode methode_A, die mit der Annotation @Deprecated versehen wurde:

/* A_Deprecated.java */
public class A_Deprecated {
  /**
   * @deprecated Die Methode methode_A wurde seit Version 1.5 als
   *             deprecated eigestuft.
   */
   @Deprecated
   public static void methode_A () {
     System.out.println("Diese Methode wurde als deprecated eingestuft.");
   }  
}

Der Methodendeklaration wurde auch ein Javadoc-Kommentar (beginnt mit den Schlüsselzeichen "/**") vorangestellt, wobei das Schlüsselwort @deprecated verwendet wurde. Dabei ist zu beachten, dass das Schlüsselwort im Kommentar mit "d" beginnt. Ein derartig angegebener Kommentar kann sinnvoll sein, um in der API-Dokumentation der Klasse eine Bemerkung dazu zu machen, weshalb die Methode als "deprecated" eingestuft wurde:

Abbildung 2.2. API-Dokumentation A_Deprecated.html (Ausschnitt) von A_Deprecated.java (nach Aufruf von javadoc A_Deprecated.java).

javadocadeprecatedjava.jpg

Mit Hilfe der folgenden Testklasse soll die mit @Deprecated markierte Methode aufgerufen werden:

/* TestDeprecated.java */
public class TestDeprecated {
  public static void main(String[] args) {
    A_Deprecated.methode_A();
  }  
}

Wird nun TestDeprecated.java mittels javac compiliert, erscheint eine durch den Compiler erzeugte Hinweismeldung, die vor der Nutzung der als "deprecated" eingestuften Methode warnt.

Note: TestDeprecated.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.

Durch die Compilierung mit der Option -Xlint:deprecation wird dann auch die Warnung im Konsolenfenster ausgegeben. Die Anweisung

> javac -Xlint:deprecation TestDeprecated.java

führt zur Ausgabe von:

TestDeprecated.java:4: warning: [deprecation] methode_A() in A_Deprecated
has been deprecated
     A_Deprecated.methode_A();
                 ^
1 warning

2.4.2. Override

Wenn eine Klasse eine andere erweitert, werden auch die Methoden der Superklasse automatisch übernommen. Es besteht aber auch die Möglichkeit, eine geerbte Methode neu zu definieren. Die geerbte Methode wird überschrieben (überlagert). Um eine Fehlermeldung zu erhalten, falls eine Methode in einer abgeleiteten Klasse nicht die entsprechende Methode ihrer Superklasse überschreibt (Stichwort: Signatur vom Methoden), ist die Annotation @Override hilfreich:

java.lang 
Annotation Type Override
1.5

@Target(value=METHOD)
@Retention(value=SOURCE)
public @interface Override

Der Einsatz von @Override zielt auf die Markierung von Methoden, denn das Element value der Meta-Annotation @Target ist mit der Konstanten METHOD belegt. Als Beispiel sollen zwei Klassen erstellt werden, die die Verwendung von @Override verdeutlichen:

/* A_Override.java */
public class A_Override {
  public static void methode_A() {
    System.out.println("Diese Methode soll ueberlagert werden."); 
  }
}

Aus der Klasse A_Override soll eine neue Klasse ableitet werden, in der die geerbte Methode methode_A überlagert werden soll. Innerhalb der folgenden Klasse wird der Methodenname methode_a verwendet (es wurde "a" anstelle von "A" geschrieben), um den Compiler zu einer Fehlermeldung zu veranlassen, da @Override verwendet wurde, aber die Methode methode_A der Superklasse nicht wirklich überlagert wurde:

/* B_Override.java */
public class B_Override extends A_Override {
  @Override
  public static void methode_a() {
    System.out.println("Diese Methode soll methode_A aus der " + 
                       "Klasse A_Override ueberlagern."); 
  } 
  public static void main(String[] args) {
    B_Override.methode_A();   
  }  
}

Die Compilierung von B_Override.java führt dann zu der erwarteten Fehlermeldung:

> javac B_Override.java
B_Override.java:3: method does not override a method from its superclass
  @Override
   ^
1 error

2.4.3. SuppressWarnings

Der Java-Compiler gibt z.B. eine Warnmeldung aus, wenn eine als "deprecated" eingestufte Methode verwendet wurde, oder wenn die break-Anweisung innerhalb des switch-Konstrukts nicht verwendet wurde. Die unterschiedlichen Warnmeldungen werden u.a. in Kategorien mit den Schlüsselwörtern deprecation, fallthrough, unchecked und all eingeteilt, die auch durch die Annotation @SuppressWarnings unterstützt werden.

java.lang 
Annotation Type SuppressWarnings
1.5
      
@Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(value=SOURCE)
public @interface SuppressWarnings

Element:
  String[] value

Die Verwendung dieser Annotation veranlasst den Compiler bestimmte Warnmeldungen zu unterdrücken. Jede Warnmeldung sollte überprüft werden und falls die Meldung aus bestimmten Gründen nicht weiter relevant ist kann sie durch diese Annotation unterdrückt werden. Das nächste Beispiel unterdrückt eine "unchecked"-Warnung, die dadurch entsteht, dass der Aufruf von add nicht eindeutig festlegt, welche Objekttypen in einen Vector aufgenommen werden können. Es ist also unklar welche Objekte an welcher Stelle des Vector gespeichert werden. Wird später auf die Elemente des Vector zugegriffen muss eine geeignete Typkonvertierung (cast-Operator) im Programm erfolgen.

/* A_SuppressWarnings.java */
import java.util.*;
public class A_SuppressWarnings {
  @SuppressWarnings("unchecked")
  public static void main(String[] args) {
    Vector v = new Vector();
    v.add(new Integer(3));
    Integer i = (Integer)v.iterator().next();
  }  
}

Um die Generierung einer Warnmeldung zu vermeiden (könnte und müsste dann auch nicht unterdrückt werden), könnten die folgenden Programmzeilen verwendet werden, die auf Generics zurückgreifen:

Vector<Integer> v = new Vector<Integer>();
v.add(new Integer(3));
Integer i = v.iterator().next();

@SuppressWarnings hat als Element eine String-Array-Variable mit dem Namen value. Dieses Array kann mit den Kategorie-Schlüsselwörtern für Warnmeldungen gefüllt werden. Da die Annotation @SuppressWarnings nur dieses Element besitzt, kann der Zusatz value=, wie auch in der Beispielklasse, weggelassen werden. Besitzt das Array value auch nur ein Element können auch die geschweiften Klammern entfallen. Die folgenden Annotationsangaben wären gleichbedeutend:

@SuppressWarnings(value = {"unchecked"})
@SuppressWarnings({"unchecked"})
@SuppressWarnings("unchecked")

Bei der Deklaration von @SuppressWarnings wird die Meta-Annotation @Target verwendet, um die Annotation zur Unterdrückung von Warnungen für eine Vielzahl von Programmelementen wie z.B. Methoden, Paramter und Konstruktoren zuzulassen.

Versionen (JDK 5): Die Annotation @SuppressWarnings wird durch JDK 5.0 Update 5 nicht vollständig unterstützt.

2.4.4. Documented

Annotationen zu Klassen oder Methoden erscheinen standarmäßig nicht in der API-Dokumentation (Javadoc) der verwendenden Klasse. Soll eine Annotation dennoch mit in die Javadocs aufgenommen werden, kann dies dadurch ermöglicht werden, dass bei der Deklaration ihres Typs @Documented verwendet wird.

java.lang.annotation 
Annotation Type Documented
1.5
      
@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Documented

Bei der Definition des eigenen Annotationstyps Autor wurde z.B. so verfahren (Ausschnitt aus Listing 2.1):

@Documented
...
public @interface Autor { ... }

Wird nun eine derart deklarierter Annotationstyp innerhalb eine Listings verwendet und anschließend von diesem Listing mittels javadoc eine API-Dokumentation erstellt, erfolgt eine Aufnahme der Annotation in die Dokumentation. Die API-Dokumentation zu (Ausschnitt aus Listing 2.3)

public class AnnotationExample {
  @Autor(id = 1, name = "im Glueck", vorname = "Hans")
  public static void methodeVonHansImGlueck() { ... }
  ...
}

würde dann wie folgt erzeugt werden (in der Dokumentation wird die Annotation @Autor innerhalb von "Method Detail" übernommen):

Abbildung 2.3. API-Dokumentation (Ausschnitt) von AnnotationExample.java (nach Aufruf von javadoc AnnotationExample.java).

javadocannotationexamplejava.jpg

2.4.5. Inherited

Eine neue Klasse die eine bestehende Klasse erweitert erbt u.a. automatisch die Methoden der bestehenden Klasse. Annotationen, die in einer bestehenden Klasse verwendet wurden, werden hingegen nicht automatisch in eine neue Unterklasse vererbt. Damit aber eine Annotation bei der Ableitung einer Klasse vererbt wird, kann bei der Deklaration ihres Annotationstyps die Meta-Annotation @Inherited verwendet werden.

java.lang.annotation 
Annotation Type Inherited
1.5
      
@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Inherited

Eine kurze Beispielbetrachtung soll die Vererbung von Annotationen erläutern. Zunächst wird die neue Annotation @TestClass erstellt. Dabei wird @Inherited verwendet, um eine Vererbung der Annotation zu ermöglichen.

/* TestClass.java */
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface TestClass { }

Die folgende Klasse, die später abgeleitet werden soll, wird durch die neu erstellte Annotation markiert:

/* A_Inherited.java */
@TestClass
public class A_Inherited {
  public void ausgabe() {
    System.out.print("Klasse A_Inherited: ");
  }  
}

A_Inherited wird nun abgeleitet und dabei wird auch @TestClass vererbt.

/* B_Inherited.java */
public class B_Inherited extends A_Inherited {
  public void ausgabe() {
    System.out.print("Klasse B_Inherited: "); 
  }  
}

Die abgeleitete Klasse B_Inherited ist also ebenfalls mit der Annotation @TestClass markiert. Um das zu überprüfen kann das folgende kurze Testprogramm verwendet werden. Zunächst werden von beiden Klassen Instanzen erzeugt. Anschließend wird mit Hilfe der Methoden getClass und isAnnotationPresent überprüft, ob bei den jeweiligen Klassen die Annotation @TestElement verfügbar ist.

/* TestInherited.java */
public class TestInherited {
  public static void main(String[] args) {
    A_Inherited ai = new A_Inherited();
    B_Inherited bi = new B_Inherited();
    ai.ausgabe();
    if (ai.getClass().isAnnotationPresent(TestClass.class)) {
      System.out.println("@TestClass in Klasse A_Inherited verfuegbar!");
    }
    bi.ausgabe();
    if (bi.getClass().isAnnotationPresent(TestClass.class)) {
      System.out.println("@TestClass in Klasse B_Inherited verfuegbar!");
    }
  }
}

Die Ausgabe des vorhergenden Testprogramms zeigt, dass die Annotation @TestClass vererbt wurde:

Klasse A_Inherited: @TestClass in Klasse A_Inherited verfuegbar!
Klasse B_Inherited: @TestClass in Klasse B_Inherited verfuegbar!

2.4.6. Target

Die Meta-Annotation @Target wird bei der Deklaration benutzerdefinierter Annotationstypen verwendet, um diese für bestimmte Programmelemente anwendbar zu machen. Die Annotation @Target wird z.B. selbst bei der Definition ihres eigenen Typs mit der Konstanten ElementType.ANNOTATION_TYPE verwendet. Diese Konstante legt fest, dass @Target ausschließlich bei der Deklaration von Annotationstypen verwendet werden kann. Die Meta-Annotation kann daher nicht für Programmelemente wie z.B. Klassen oder Methoden verwendet werden.

java.lang.annotation 
Annotation Type Target
1.5
      
@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Target

Element:
  ElementType[] value

Die Annotation @Target besitzt einen Paramter vom Typ ElementType. Die Konstanten, die die Anwendbarkeit von benutzerdefinierten Annotationen, bei deren Typ-Deklaration @Target verwendet wurde, auf bestimmte Programmelemente festlegen, sind in der ElementType-Aufzählung aufgeführt:

java.lang.annotation 
Enum ElementType
1.5

Enum Constant:
  ANNOTATION_TYPE 
  CONSTRUCTOR
  FIELD 
  LOCAL_VARIABLE 
  METHOD 
  PACKAGE 
  PARAMETER 
  TYPE

Die Bezeichnungen der Konstanten deuten auf die Programmelemente hin, für die die benutzerdefinierte Annotation gelten soll. Die Konstante ElementType.TYPE wird verwendet, falls eine Annotation nur für die Programmeelemente class, interface und enum zulässig sein soll. Bei der Deklaration des Annotationstyps Autor wurde z.B. mit @Target festgelegt, dass @Autor nur bei der Deklaration von Methoden zulässig ist (Ausschnitt von Listing 2.1):

@Target(ElementType.METHOD)
public @interface Autor { ... }

Eine Verwendung von @Autor bei der Klassendeklaration in Listing 2.3 wie z.B.

@Autor(id = 1, name = "im Glueck", vorname = "Hans")  // Fehler
public class AnnotationExample { ... }

würde bei der Compilierung zu einer Fehlermeldung führen:

AnnotationExample.java:9: annotation type not applicable to this 
kind of declaration
@Autor(id = 1, name = "im Glueck", vorname = "Hans")
                                             ^
1 error

2.4.7. Retention

Mit der Meta-Annotation @Retention kann die "Verfügbarkeit" einer benutzerdefinierten Annotation festgelegt werden. Wird bei der Erstellung einer neuen Annotation @Retention mit einem entsprechenden Argument verwendet, ist die erstellte Annotation z.B. während der Laufzeit in dem Programm "verfügbar", das diese benutzerdefinierte Annotation verwendet.

java.lang.annotation 
Annotation Type Retention
1.5

@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Retention

Element:
  RetentionPolicy value

Die Meta-Annotation akzeptiert einen einzigen Paramter vom Typ RetentionPolicy. Die Konstanten dieses Aufzählungstyps legen die Art der Aufrechterhaltung (Beibehaltung) für neu erstellte Annotationen fest.

java.lang.annotation 
Enum RetentionPolicy
1.5

Enum Constant:
  CLASS
  RUNTIME
  SOURCE

Die drei unterschiedlichen Konstanten sind dabei entsprechend maßgebend, ob eine benutzerdefinierte Annotation, in den durch die Compilierung erzeugten Bytecode (Klassendatei) des verwendenden Programms, aufgenommen wird:

  • RetentionPolicy.SOURCE. Annotationen, bei deren Typ-Deklaration @Retention(RetentionPolicy.SOURCE) verwendet wurde, werden bei der Compilierung des verwendenden Java-Quelltextes verworfen und sind damit auch nicht im Bytecode der durch die Compilierung entstehenden Klassendatei enthalten. @Override und @SuppressWarnings sind Beispiele für derart deklarierte Annotationen.

  • RetentionPolicy.CLASS. (Standardeinstellung) Annotationen, bei deren Typ-Deklaration diese Konstante verwendet wurde, werden durch den Compiler ins resultierende Klassenfile des verwendenden Programms geschrieben. Sie sind aber im Bytecode so gekennzeichnet, dass sie während der Laufzeit der Klassendatei nicht zur Verfügung stehen.

  • RetentionPolicy.RUNTIME. Befinden sich Annotationen, deren Typ mit @Retention(RetentionPolicy.RUNTIME) deklariert wurden, im Java-Quelltext, werden diese bei der Compilierung des Quelltextes in den Bytecode des entstehenden Klassenfiles derart geschrieben, dass sie von der VM zur Laufzeit aufrecht gehalten werden, und z.B. mit Methoden des Interfaces AnnotatedElement ausgelesen werden können.

Bei der Deklaration des Annotationstyps Autor wurde z.B. @Retention mit der Konstanten RetentionPolicy.RUNTIME verwendet (Ausschnitt aus Listing 2.1):

@Retention(RetentionPolicy.RUNTIME)
...
public @interface Autor { ... }

Diese Annotation wurde anschließend in Listing 2.3 verwendet, um eine Methode zu kennzeichnen (Ausschnitt):

public class AnnotationExample {
  @Autor(id = 1, name = "im Glueck", vorname = "Hans")
  public static void methodeVonHansImGlueck() { ... }
  ...
}

Die Auswirkungen der Konstanten RetentionPolicy.RUNTIME auf den Bytcode der Datei AnnotationExample.class zeigt der folgende Hexdump-Ausschnitt:

ca fe ba be 00 00 00 31 00 63 0a 00 1b 00 32 09     .......1.c....2.     0
...
65 01 00 16 6d 65 74 68 6f 64 65 56 6f 6e 48 61     e...methodeVonHa     160
6e 73 49 6d 47 6c 75 65 63 6b 01 00 19 52 75 6e     nsImGlueck...Run     176
74 69 6d 65 56 69 73 69 62 6c 65 41 6e 6e 6f 74     timeVisibleAnnot     192
61 74 69 6f 6e 73 01 00 07 4c 41 75 74 6f 72 3b     ations...LAutor;     208
01 00 02 69 64 03 00 00 00 01 01 00 04 6e 61 6d     ...id........nam     224
65 01 00 09 69 6d 20 47 6c 75 65 63 6b 01 00 07     e...im Glueck...     240
76 6f 72 6e 61 6d 65 01 00 04 48 61 6e 73 01 00     vorname...Hans..     256
...

Die Repräsentation der Zeichenfolge RuntimeVisibleAnnotations lautet in hexadezimaler Schreibweise in ASCII-Codierung: 52 75 6e 74 69 6d 65 56 69 73 69 62 6c 65 41 6e 6e 6f 74 61 74 69 6f 6e 73. Diese Bytefolge findet sich im Bytecode der betrachteten Klasse wieder. Die Verwendung der Konstanten RetentionPolicy.CLASS bei der Deklaration von Autor führt hingegen zu einer Bytefolge an geeigneter Stelle im Klassenfile (Hexdump) die der hexadezimalen Repräsentation von RuntimeInvisibleAnnotations nach ASCII-Codierung entspricht und deutet darauf hin, dass die Sichtbarkeit der Annotation während der Laufzeit nicht vorhanden sein soll.

 

 

 

Diese Seite nutzt Google-Dienste - siehe dazu Datenschutz.

Copyright © 2006, 2007 Harald Roeder