Java-Anmerkung - Java annotation

In der Programmiersprache Java ist eine Annotation eine Form syntaktischer Metadaten , die dem Java- Quellcode hinzugefügt werden können . Klassen , Methoden , Variablen , Parameter und Java-Pakete können mit Anmerkungen versehen werden. Wie Javadoc- Tags können Java-Annotationen aus Quelldateien gelesen werden. Im Gegensatz zu Javadoc- Tags können Java-Annotationen auch in vom Java-Compiler generierte Java-Klassendateien eingebettet und daraus gelesen werden . Dadurch können Annotationen von der Java Virtual Machine zur Laufzeit beibehalten und über Reflection gelesen werden . Es ist möglich, Meta-Annotationen aus den vorhandenen in Java zu erstellen.

Geschichte

Die Java-Plattform verfügt über verschiedene Ad-hoc- Annotationsmechanismen – zum Beispiel den transientModifikator oder das @deprecatedjavadoc-Tag. Die Java Specification Request JSR-175 führte 2002 die Allzweck-Annotation (auch als Metadaten bekannt ) in den Java Community Process ein; es wurde im September 2004 genehmigt. Annotationen wurden in der Sprache selbst ab Version 1.5 des Java Development Kit (JDK) verfügbar . Das aptTool stellte eine vorläufige Schnittstelle für die Verarbeitung von Anmerkungen zur Kompilierzeit in JDK Version 1.5 bereit; JSR-269 hat dies formalisiert und in Version 1.6 in den Javac- Compiler integriert .

Integrierte Anmerkungen

Java definiert eine Reihe von Anmerkungen, die in die Sprache integriert sind. Von den sieben Standardannotationen sind drei Teil von java.lang und die restlichen vier werden aus java.lang.annotation importiert.

Auf Java-Code angewendete Anmerkungen:

Anmerkungen, die auf andere Anmerkungen angewendet werden (auch als "Meta-Anmerkungen" bezeichnet):

  • @Retention - Gibt an, wie die markierte Annotation gespeichert wird, sei es nur im Code, in die Klasse kompiliert oder zur Laufzeit durch Reflektion verfügbar.
  • @Documented - Markiert eine weitere Anmerkung zur Aufnahme in die Dokumentation.
  • @Target - Markiert eine andere Anmerkung, um einzuschränken, auf welche Art von Java-Elementen die Anmerkung angewendet werden kann.
  • @Inherited - Markiert eine andere Annotation, die an Unterklassen der annotierten Klasse vererbt werden soll (standardmäßig werden Annotationen nicht an Unterklassen geerbt).

Seit Java 7 wurden der Sprache drei zusätzliche Anmerkungen hinzugefügt.

  • @SafeVarargs- Unterdrückt Warnungen für alle Anrufer eines Verfahrens oder Konstruktor mit einem Generika varargs Parameter, da Java 7.
  • @FunctionalInterface - Gibt an, dass die Typdeklaration eine funktionale Schnittstelle sein soll , seit Java 8.
  • @Repeatable - Gibt an, dass die Annotation seit Java 8 mehr als einmal auf dieselbe Deklaration angewendet werden kann.

Beispiel

Integrierte Anmerkungen

Dieses Beispiel demonstriert die Verwendung der @OverrideAnnotation. Es weist den Compiler an, Elternklassen auf übereinstimmende Methoden zu überprüfen. In diesem Fall wird ein Fehler generiert, da die gettype()Methode der Klasse Cat die getType()Klasse Animal nicht wie gewünscht überschreibt , weil case nicht übereinstimmt . Wenn die @OverrideAnnotation fehlt, wird gettype()in der Klasse Cat eine neue Methode des Namens erstellt.

public class Animal {
    public void speak() {
    }

    public String getType() {
        return "Generic animal";
    }
}

public class Cat extends Animal {
    @Override
    public void speak() { // This is a good override.
        System.out.println("Meow.");
    }

    @Override
    public String gettype() { // Compile-time error due to typo: should be getType() not gettype().
        return "Cat";
    }
}

Benutzerdefinierte Anmerkungen

Annotationstypdeklarationen ähneln normalen Schnittstellendeklarationen. Ein At-Zeichen (@) steht vor dem Interface- Schlüsselwort . Jede Methodendeklaration definiert ein Element des Annotationstyps. Methodendeklarationen dürfen keine Parameter oder eine throws-Klausel enthalten. Rückgabetypen sind auf Primitive , String , Class, enums , Annotations und Arrays der vorhergehenden Typen beschränkt. Methoden können Standardwerte haben .

  // @Twizzle is an annotation to method toggle().
  @Twizzle
  public void toggle() {
  }

  // Declares the annotation Twizzle.
  public @interface Twizzle {
  }

Anmerkungen können eine optionale Liste von Schlüssel-Wert-Paaren enthalten:

  // Same as: @Edible(value = true)
  @Edible(true)
  Item item = new Carrot();

  public @interface Edible {
      boolean value() default false;
  }

  @Author(first = "Oompah", last = "Loompah")
  Book book = new Book();

  public @interface Author {
      String first();
      String last();
  }

Anmerkungen selbst können mit Anmerkungen versehen werden, um anzugeben, wo und wann sie verwendet werden können:

  @Retention(RetentionPolicy.RUNTIME) // Make this annotation accessible at runtime via reflection.
  @Target({ElementType.METHOD})       // This annotation can only be applied to class methods.
  public @interface Tweezable {
  }

Der Compiler reserviert einen Satz spezieller Anmerkungen (einschließlich @Deprecated, @Overrideund @SuppressWarnings) für syntaktische Zwecke.

Annotationen werden häufig von Frameworks verwendet, um Verhaltensweisen bequem auf benutzerdefinierte Klassen und Methoden anzuwenden, die ansonsten in einer externen Quelle (z. B. einer XML-Konfigurationsdatei) oder programmgesteuert (mit API-Aufrufen) deklariert werden müssen. Das Folgende ist beispielsweise eine annotierte JPA- Datenklasse:

@Entity                                             // Declares this an entity bean
@Table(name = "people")                             // Maps the bean to SQL table "people"
public class Person implements Serializable {
    @Id                                             // Map this to the primary key column.
    @GeneratedValue(strategy = GenerationType.AUTO) // Database will generate new primary keys, not us.
    private Integer id;

    @Column(length = 32)                            // Truncate column values to 32 characters.
    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Die Annotationen sind keine Methodenaufrufe und werden von sich aus nichts tun. Stattdessen wird das Klassenobjekt zur Laufzeit an die JPA- Implementierung übergeben , die dann die Annotationen extrahiert, um eine objektrelationale Zuordnung zu generieren .

Ein vollständiges Beispiel ist unten angegeben:

package com.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD,
         ElementType.CONSTRUCTOR,ElementType.ANNOTATION_TYPE,
         ElementType.PACKAGE,ElementType.FIELD,ElementType.LOCAL_VARIABLE})
@Inherited

public @interface Unfinished {
    public enum Priority { LOW, MEDIUM, HIGH }
    String value();
    String[] changedBy() default "";
    String[] lastChangedBy() default "";
    Priority priority() default Priority.MEDIUM;
    String createdBy() default "James Gosling";
    String lastChanged() default "2011-07-08";
}
package com.annotation;

public @interface UnderConstruction {
    String owner() default "Patrick Naughton";
    String value() default "Object is Under Construction.";
    String createdBy() default "Mike Sheridan";
    String lastChanged() default "2011-07-08";
}
package com.validators;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;

import com.annotation.UnderConstruction;
import com.annotation.Unfinished;
import com.annotation.Unfinished.Priority;
import com.util.Util;

@UnderConstruction(owner="Jon Doe")
public class DateValidator implements Validator {
	
    public void validate(FacesContext context, UIComponent component, Object value)
        throws ValidatorException {
        String date = (String) value;
        String errorLabel = "Please enter a valid date.";
        if (!component.getAttributes().isEmpty()) {
            errorLabel = (String) component.getAttributes().get("errordisplayval");
        }

        if (!Util.validateAGivenDate(date)) {
            @Unfinished(changedBy = "Steve",
                value = "whether to add message to context or not, confirm",
                priority = Priority.HIGH
            )
            FacesMessage message = new FacesMessage();
            message.setSeverity(FacesMessage.SEVERITY_ERROR);
            message.setSummary(errorLabel);
            message.setDetail(errorLabel);
            throw new ValidatorException(message);
        }
    }
}

wird bearbeitet

Wenn Java-Quellcode kompiliert wird, können Annotationen von Compiler-Plug-Ins verarbeitet werden, die als Annotationsprozessoren bezeichnet werden. Prozessoren können Informationsnachrichten erzeugen oder zusätzliche Java-Quelldateien oder -Ressourcen erstellen, die wiederum kompiliert und verarbeitet werden können. Anmerkungsprozessoren können jedoch den Anmerkungscode selbst nicht ändern. (Code - Modifikationen können unter Verwendung von Methoden jenseits der Java Language Specification implementiert.) Die Java - Compiler speichern bedingt Anmerkung Metadaten in den Klassendateien, wenn die Anmerkung a hat RetentionPolicyvon CLASSoder RUNTIME. Später können die JVM oder andere Programme nach den Metadaten suchen, um zu bestimmen, wie mit den Programmelementen zu interagieren oder ihr Verhalten zu ändern.

Zusätzlich zur Verarbeitung einer Annotation mit einem Annotationsprozessor kann ein Java-Programmierer seinen eigenen Code schreiben, der Reflexionen verwendet, um die Annotation zu verarbeiten. Java SE 5 unterstützt eine neue Schnittstelle, die im java.lang.reflectPaket definiert ist . Dieses Paket enthält die Schnittstelle aufgerufen AnnotatedElement, die von den Java Reflexionsklassen einschließlich implementiert ist Class, Constructor, Field, Method, und Package. Die Implementierungen dieser Schnittstelle werden verwendet, um ein kommentiertes Element des Programms darzustellen, das derzeit in der Java Virtual Machine läuft. Diese Schnittstelle ermöglicht das reflektierende Lesen von Annotationen.

Die AnnotatedElementSchnittstelle bietet Zugriff auf Anmerkungen mit RUNTIMEAufbewahrung. Dieser Zugriff wird durch die Methoden getAnnotation, getAnnotations, und bereitgestellt isAnnotationPresent. Da Annotationstypen wie Klassen kompiliert und in Bytecode-Dateien gespeichert werden, können die von diesen Methoden zurückgegebenen Annotationen wie jedes normale Java-Objekt abgefragt werden. Nachfolgend finden Sie ein vollständiges Beispiel für die Verarbeitung einer Anmerkung:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

// This is the annotation to be processed
// Default for Target is all Java Elements
// Change retention policy to RUNTIME (default is CLASS)
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeHeader {
    // Default value specified for developer attribute
    String developer() default "Unknown";
    String lastModified();
    String [] teamMembers();
    int meaningOfLife();
}
// This is the annotation being applied to a class
@TypeHeader(developer = "Bob Bee",
    lastModified = "2013-02-12",
    teamMembers = { "Ann", "Dan", "Fran" },
    meaningOfLife = 42)

public class SetCustomAnnotation {
    // Class contents go here
}
// This is the example code that processes the annotation
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;

public class UseCustomAnnotation {
    public static void main(String [] args) {
        Class<SetCustomAnnotation> classObject = SetCustomAnnotation.class;
        readAnnotation(classObject);
    }

    static void readAnnotation(AnnotatedElement element) {
        try {
            System.out.println("Annotation element values: \n");
            if (element.isAnnotationPresent(TypeHeader.class)) {
                // getAnnotation returns Annotation type
                Annotation singleAnnotation = 
                        element.getAnnotation(TypeHeader.class);
                TypeHeader header = (TypeHeader) singleAnnotation;

                System.out.println("Developer: " + header.developer());
                System.out.println("Last Modified: " + header.lastModified());

                // teamMembers returned as String []
                System.out.print("Team members: ");
                for (String member : header.teamMembers())
                    System.out.print(member + ", ");
                System.out.print("\n");

                System.out.println("Meaning of Life: "+ header.meaningOfLife());
            }
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }
}

Nutzung in freier Wildbahn

Forscher haben die Verwendung von Java-Annotationen in 1.094 bemerkenswerten Open-Source-Java-Projekten untersucht, die auf GitHub gehostet werden. Sie fanden heraus, dass Anmerkungen aktiv gepflegt werden, wobei viele Anmerkungen hinzugefügt, aber auch aufgrund von Fehlern im Anmerkungstyp oder in den Werten geändert oder entfernt werden. Insgesamt stellt diese Studie fest, dass es einen kleinen, aber signifikanten Zusammenhang zwischen der Verwendung von Annotationen und der Fehleranfälligkeit des Codes gibt: Java-Code mit Annotationen ist tendenziell weniger fehleranfällig.

Siehe auch

Verweise

Externe Links