Java-Annotationen: Metadaten mit Klassen, Methoden und anderen Elementen verknüpfen
Einführung in Java-Annotationen
In der Programmierung gibt es Situationen, in denen Metadaten mit Klassen, Methoden oder anderen Elementen im Java-Code verknüpft werden müssen. Zum Beispiel könnte ein Team unvollendete Klassen in einer großen Anwendung identifizieren müssen. Für jede unvollendete Klasse könnten die Metadaten den Namen des Entwicklers, der für den Abschluss der Klasse verantwortlich ist, sowie das voraussichtliche Fertigstellungsdatum enthalten.
Vor Java 5 waren Kommentare das einzige flexible Mittel, um Metadaten mit Anwendungselementen zu verknüpfen. Da der Compiler diese jedoch ignoriert, sind Kommentare zur Laufzeit nicht verfügbar. Selbst wenn sie verfügbar wären, müssten Entwickler den Text analysieren, um wichtige Daten zu extrahieren. Ohne eine Standardisierung, wie die Daten angegeben werden, könnte dies unmöglich sein.
Java 5 hat alles verändert, indem es Annotationen eingeführt hat, ein standardisiertes Verfahren zur Verknüpfung von Metadaten mit verschiedenen Anwendungselementen. Dieses Tutorial führt in die Welt der Java-Annotationen ein.
Was in diesem Java-Tutorial gelernt wird
- Die vier Elemente einer Java-Annotation
- Verwendung von
@interface
zur Deklaration von Annotationstypen - Meta-Annotationstypen und das Problem der Flexibilität
- Wie man Annotationen verarbeitet
- Die vordefinierten Annotationstypen in Java
Elemente einer Java-Annotation
Eine Annotation in Java besteht aus vier Elementen:
- Ein
@interface
-Mechanismus zur Deklaration von Annotationstypen. - Meta-Annotationstypen, die verwendet werden können, um die Anwendungselemente zu identifizieren, auf die ein Annotationstyp anwendbar ist, sowie um die Lebensdauer einer Annotation zu bestimmen.
- Unterstützung für die Verarbeitung von Annotationen über eine Erweiterung der Java-Reflexions-API und ein allgemeines Werkzeug zur Verarbeitung von Annotationen.
- Die standardmäßigen (vordefinierten) Annotationstypen in Java.
Entwickler lernen, wie jedes dieser Elemente in Java-Annotationen verwendet werden kann.
Verwendung von @interface zur Deklaration von Annotationstypen
Ein Annotationstyp kann deklariert werden, indem das @
-Symbol gefolgt vom reservierten Wort interface
und einem Bezeichner angegeben wird. Zum Beispiel deklariert das folgende Beispiel einen einfachen Annotationstyp, der verwendet werden könnte, um thread-sicheren Code zu annotieren.
Beispiel 1: ThreadSafe.java
public @interface ThreadSafe {}
Nach der Deklaration dieses Annotationstyps können die Methoden, die als thread-sicher betrachtet werden, mit Instanzen dieses Typs versehen werden, indem das @
-Symbol gefolgt vom Typnamen zu den Methodenköpfen hinzugefügt wird.
Beispiel 2: AnnDemo.java (Version 1)
public class AnnDemo {
@ThreadSafe
public static void main(String[] args) {
// Thread-sicherer Code hier
}
}
Die ThreadSafe
-Instanzen liefern keine Metadaten außer dem Namen des Annotationstyps. Metadaten können jedoch bereitgestellt werden, indem Elemente zu diesem Typ hinzugefügt werden, wobei ein Element ein Methodenkopf ist, der im Körper des Annotationstyps platziert wird.
Beispiel 3: ToDo.java (Version 1)
public @interface ToDo {
int id();
String finishDate();
String coder() default "n/a";
}
Jedes Element deklariert keine Parameter oder throws-Klausel, hat einen legalen Rückgabetyp (int
oder String
) und endet mit einem Semikolon. Das letzte Element zeigt, dass ein Standardrückgabewert angegeben werden kann; dieser Wert wird zurückgegeben, wenn einer Annotation kein Wert für das Element zugewiesen wird.
Beispiel 4: AnnDemo.java (Version 2)
public class AnnDemo {
public static void main(String[] args) {
String[] cities = { "New York", "Melbourne", "Beijing", "Moscow", "Paris", "London" };
sort(cities);
}
@ToDo(id = 1000, finishDate = "10/10/2019", coder = "John Doe")
static void sort(Object[] objects) {
// Sortierlogik hier
}
}
In diesem Beispiel wird jedem Element eine Metadateneigenschaft zugewiesen; zum Beispiel wird 1000
dem id
-Element zugewiesen. Im Gegensatz zum coder
-Element müssen die Elemente id
und finishDate
angegeben werden; andernfalls meldet der Compiler einen Fehler.
Meta-Annotationstypen und das Problem der Flexibilität
Es können Typen (z.B. Klassen), Methoden, lokale Variablen und mehr annotiert werden. Diese Flexibilität kann jedoch problematisch sein. Beispielsweise möchten Entwickler möglicherweise ToDo
nur auf Methoden beschränken, aber nichts hindert sie daran, es auch auf andere Anwendungselemente anzuwenden.
Beispiel 5: AnnDemo.java (Version 4)
@ToDo(id = 1000, finishDate = "10/10/2019", coder = "John Doe")
public class AnnDemo {
public static void main(String[] args) {
@ToDo(id = 1001, finishDate = "11/11/2019", coder = "Jane Doe")
String[] cities = { "New York", "Melbourne", "Beijing", "Moscow", "Paris", "London" };
sort(cities);
}
@ToDo(id = 1000, finishDate = "10/10/2019", coder = "John Doe")
static void sort(Object[] objects) {
// Sortierlogik hier
}
}
In diesem Beispiel wird ToDo
auch verwendet, um die Klasse AnnDemo
und die lokale Variable cities
zu annotieren. Diese fehlerhaften Annotationen könnten jemanden, der den Code überprüft, verwirren oder sogar die eigenen Annotation-Verarbeitungswerkzeuge.
Zusätzliche Meta-Annotationstypen
Java 5 führte drei wichtige Meta-Annotationstypen ein, die im java.lang.annotation
-Paket zu finden sind:
Retention
: Gibt an, wie lange Annotationen mit dem annotierten Typ beibehalten werden sollen.Documented
: Gibt an, dass Instanzen vonDocumented
-annotierten Annotationen vonjavadoc
und ähnlichen Tools dokumentiert werden sollen.Inherited
: Gibt an, dass ein Annotationstyp automatisch vererbt wird.
Wie man Annotationen verarbeitet
Annotationen sind dazu gedacht, verarbeitet zu werden; andernfalls macht es keinen Sinn, sie zu haben. Java 5 erweiterte die Java-Reflexions-API, um Entwicklern zu helfen, eigene Annotation-Verarbeitungswerkzeuge zu erstellen. Zum Beispiel deklariert Class
eine Methode Annotation[] getAnnotations()
, die ein Array von java.lang.Annotation
-Instanzen zurückgibt.
Beispiel 6: AnnProcDemo.java
import java.lang.reflect.Method;
public class AnnProcDemo {
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("usage: java AnnProcDemo classfile");
return;
}
Method[] methods = Class.forName(args[0]).getMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].isAnnotationPresent(ToDo.class)) {
ToDo todo = methods[i].getAnnotation(ToDo.class);
System.out.printf("ID = %d%n", todo.id());
System.out.printf("Finish date = %s%n", todo.finishDate());
System.out.printf("Coder = %s%n%n", todo.coder());
}
}
}
}
Nachdem sichergestellt wurde, dass genau ein Befehlszeilenargument (das eine Klassendatei identifiziert) angegeben wurde, lädt main()
die Klassendatei über Class.forName()
, ruft getMethods()
auf, um ein Array von java.lang.reflect.Method
-Objekten zu erhalten, die alle public
-Methoden in der Klassendatei identifizieren, und verarbeitet diese Methoden.
Vordefinierte Annotationstypen in Java
Zusätzlich zu den Meta-Annotationstypen Target
, Retention
, Documented
und Inherited
führte Java 5 die vordefinierten Annotationstypen Deprecated
, Override
und SuppressWarnings
ein. Diese drei Annotationstypen sind ausschließlich für den Compiler konzipiert, weshalb ihre Aufbewahrungsrichtlinien auf SOURCE
festgelegt sind.
Fazit
Java-Annotationen bieten eine leistungsstarke Methode, um Metadaten mit verschiedenen Anwendungselementen zu verknüpfen. Durch die Verwendung von @interface
zur Deklaration von Annotationstypen, das Hinzufügen von Meta-Annotationstypen und die Unterstützung für die Verarbeitung von Annotationen über die Java-Reflexions-API können Entwickler flexibel und effizient arbeiten. Die standardmäßigen Annotationstypen in Java ergänzen diese Funktionalität, indem sie häufige Anforderungen direkt unterstützen.
Für weiterführende Informationen zu Java-Annotationen und deren Einsatz in verschiedenen Szenarien, besuchen Sie bitte Java oder die Oracle Java Documentation. Weitere Informationen zu verwandten Themen finden Sie in diesen interessanten Artikeln: Multithreading in JavaScript: Web Worker und Worker Threads nutzen und Ein Überblick über neue Stream Gatherers in Java 22.