Übersicht über die in Java integrierten Anmerkungen

1. Übersicht

In diesem Artikel werden wir über eine Kernfunktion der Java-Sprache sprechen - die Standardanmerkungen, die im JDK verfügbar sind.

2. Was ist eine Anmerkung?

Einfach ausgedrückt sind Anmerkungen Java-Typen, denen ein "@" vorangestellt ist .

Java hat seit der Version 1.5 Anmerkungen. Seitdem haben sie die Art und Weise geprägt, wie wir unsere Anwendungen entworfen haben.

Spring und Hibernate sind hervorragende Beispiele für Frameworks, die sich stark auf Anmerkungen stützen, um verschiedene Entwurfstechniken zu ermöglichen.

Grundsätzlich weist eine Annotation dem Quellcode, an den sie gebunden ist, zusätzliche Metadaten zu . Durch Hinzufügen einer Anmerkung zu einer Methode, Schnittstelle, Klasse oder einem Feld können wir:

  1. Informieren Sie den Compiler über Warnungen und Fehler
  2. Bearbeiten Sie den Quellcode zur Kompilierungszeit
  3. Ändern oder untersuchen Sie das Verhalten zur Laufzeit

3. Integrierte Java-Anmerkungen

Nachdem wir uns die Grundlagen angesehen haben, werfen wir einen Blick auf einige Anmerkungen, die im Lieferumfang von Java enthalten sind. Erstens gibt es mehrere, die die Zusammenstellung beeinflussen:

  1. @Override
  2. @ SuppressWarnings
  3. @ Veraltet
  4. @SafeVarargs
  5. @FunctionalInterface
  6. @ Native

Diese Anmerkungen erzeugen oder unterdrücken Compiler-Warnungen und -Fehler. Eine konsistente Anwendung ist häufig eine gute Vorgehensweise, da durch Hinzufügen zukünftige Programmiererfehler vermieden werden können.

Die Annotation @Override wird verwendet, um anzugeben, dass eine Methode das Verhalten einer geerbten Methode überschreibt oder ersetzt.

@SuppressWarnings gibt an, dass bestimmte Warnungen aus einem Teil des Codes ignoriert werden sollen. DieAnnotation @SafeVarargs wirkt sich auch auf eine Art Warnung aus, die sich auf die Verwendung von varargs bezieht.

Die Annotation @Deprecated kann verwendet werden, um eine API als nicht mehr zur Verwendung vorgesehen zu markieren. Darüber hinaus wurde diese Anmerkung in Java 9 nachgerüstet, um weitere Informationen über die Veralterung darzustellen.

Für all dies finden Sie detailliertere Informationen in den verlinkten Artikeln.

3.1. @FunctionalInterface

Mit Java 8 können wir Code funktionaler schreiben.

Single Abstract Method Interfaces sind ein großer Teil davon. Wenn wir beabsichtigen, dass eine SAM-Schnittstelle von Lambdas verwendet wird, können wir sie optional mit @FunctionalInterface als solche markieren :

@FunctionalInterface public interface Adder { int add(int a, int b); }

Wie @Override mit Methoden erklärt @FunctionalInterface unsere Absichten mit Adder .

Unabhängig davon , ob wir @FunctionalInterface verwenden oder nicht, können wir Adder weiterhin auf dieselbe Weise verwenden:

Adder adder = (a,b) -> a + b; int result = adder.add(4,5);

Wenn wir Adder jedoch eine zweite Methode hinzufügen , beschwert sich der Compiler:

@FunctionalInterface public interface Adder { // compiler complains that the interface is not a SAM int add(int a, int b); int div(int a, int b); }

Dies wäre ohne die Annotation @FunctionalInterface kompiliert worden . Also, was gibt es uns?

Wie @Override schützt uns diese Anmerkung vor zukünftigen Programmiererfehlern. Obwohl es legal ist, mehr als eine Methode auf einer Schnittstelle zu haben, ist dies nicht der Fall, wenn diese Schnittstelle als Lambda-Ziel verwendet wird. Ohne diese Anmerkung würde der Compiler an Dutzenden von Stellen brechen, an denen Adder als Lambda verwendet wurde. Jetzt bricht es nur noch in Adder selbst ein.

3.2. @ Native

Ab Java 8 gibt es im Paket java.lang.annotation eine neue Anmerkung namens Native. Die Annotation @Native gilt nur für Felder. Es zeigt an, dass das mit Anmerkungen versehene Feld eine Konstante ist, auf die aus dem nativen Code verwiesen werden kann . So wird es beispielsweise in der Integer- Klasse verwendet:

public final class Integer { @Native public static final int MIN_VALUE = 0x80000000; // omitted }

Diese Anmerkung kann auch als Hinweis für die Tools zum Generieren einiger zusätzlicher Header-Dateien dienen.

4. Meta-Annotationen

Als nächstes sind Meta-Annotationen Annotationen, die auf andere Annotationen angewendet werden können.

Diese Meta-Annotationen werden beispielsweise für die Annotationskonfiguration verwendet:

  1. @Ziel
  2. @ Zurückhaltung
  3. @Vererbt
  4. @Dokumentiert
  5. @Wiederholbar

4.1. @Ziel

Der Umfang der Anmerkungen kann je nach Anforderung variieren. Während eine Annotation nur mit Methoden verwendet wird, kann eine andere Annotation mit Konstruktor- und Felddeklarationen verwendet werden.

Um die Zielelemente einer benutzerdefinierten Annotation zu bestimmen, müssen Sie sie mit einer @ Target- Annotation kennzeichnen .

@Target kann mit acht verschiedenen Elementtypen arbeiten. Wenn wir uns den Quellcode von @ SafeVarargs ansehen , können wir sehen, dass er nur an Konstruktoren oder Methoden angehängt werden darf:

@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.CONSTRUCTOR, ElementType.METHOD}) public @interface SafeVarargs { }

4.2. @ Zurückhaltung

Einige Anmerkungen sind als Hinweise für den Compiler gedacht, während andere zur Laufzeit verwendet werden.

Wir verwenden die Annotation @Retention, um anzugeben , wo im Lebenszyklus unseres Programms unsere Annotation gilt .

To do this, we need to configure @Retention with one of three retention policies:

  1. RetentionPolicy.SOURCE – visible by neither the compiler nor the runtime
  2. RetentionPolicy.CLASS – visible by the compiler
  3. RetentionPolicy.RUNTIME – visible by the compiler and the runtime

@Retention defaults to RetentionPolicy.SOURCE.

If we have an annotation that should be accessible at runtime:

@Retention(RetentionPolicy.RUNTIME) @Target(TYPE) public @interface RetentionAnnotation { }

Then, if we add some annotations to a class:

@RetentionAnnotation @Deprecated public class AnnotatedClass { }

Now we can reflect on AnnotatedClass to see how many annotations are retained:

@Test public void whenAnnotationRetentionPolicyRuntime_shouldAccess() { AnnotatedClass anAnnotatedClass = new AnnotatedClass(); Annotation[] annotations = anAnnotatedClass.getClass().getAnnotations(); assertThat(annotations.length, is(1)); }

The value is 1 because @RetentionAnnotation has a retention policy of RUNTIME while @Deprecated doesn't.

4.3. @Inherited

In some situations, we may need a subclass to have the annotations bound to a parent class.

We can use the @Inherited annotation to make our annotation propagate from an annotated class to its subclasses.

If we apply @Inherited to our custom annotation and then apply it to BaseClass:

@Inherited @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface InheritedAnnotation { } @InheritedAnnotation public class BaseClass { } public class DerivedClass extends BaseClass { }

Then, after extending the BaseClass, we should see that DerivedClass appears to have the same annotation at runtime:

@Test public void whenAnnotationInherited_thenShouldExist() { DerivedClass derivedClass = new DerivedClass(); InheritedAnnotation annotation = derivedClass.getClass() .getAnnotation(InheritedAnnotation.class); assertThat(annotation, instanceOf(InheritedAnnotation.class)); }

Without the @Inherited annotation, the above test would fail.

4.4. @Documented

By default, Java doesn't document the usage of an annotation in Javadocs.

But, we can use the @Documented annotation to change Java's default behavior.

If we create a custom annotation that uses @Documented:

@Documented @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ExcelCell { int value(); }

And, apply it to the appropriate Java element:

public class Employee { @ExcelCell(0) public String name; }

Then, the Employee Javadoc will reveal the annotation usage:

4.5. @Repeatable

Sometimes it can be useful to specify the same annotation more than once on a given Java element.

Before Java 7, we had to group annotations together into a single container annotation:

@Schedules({ @Schedule(time = "15:05"), @Schedule(time = "23:00") }) void scheduledAlarm() { }

However, Java 7 brought a cleaner approach. With the @Repeatable annotation, we can make an annotation repeatable:

@Repeatable(Schedules.class) public @interface Schedule { String time() default "09:00"; }

To use @Repeatable, we need to have a container annotation, too. In this case, we'll reuse @Schedules:

public @interface Schedules { Schedule[] value(); }

Of course, this looks a lot like what we had before Java 7. But, the value now is that the wrapper @Schedules isn't specified anymore when we need to repeat @Schedule:

@Schedule @Schedule(time = "15:05") @Schedule(time = "23:00") void scheduledAlarm() { }

Because Java requires the wrapper annotation, it was easy for us to migrate from pre-Java 7 annotation lists to repeatable annotations.

5. Conclusion

In diesem Artikel haben wir über in Java integrierte Anmerkungen gesprochen, mit denen jeder Java-Entwickler vertraut sein sollte.

Wie immer finden Sie alle Beispiele des Artikels auf GitHub.