Anotace Java - Java annotation

V počítačovém programovacím jazyce Java je anotace formou syntaktických metadat, která lze přidat do zdrojového kódu Java . Třídy , metody , proměnné , parametry a balíčky Java mohou být anotovány. Stejně jako značky Javadoc lze anotace Java číst ze zdrojových souborů. Na rozdíl od značek Javadoc lze anotace Java také vložit a číst ze souborů tříd Java generovaných kompilátorem Java . To umožňuje udržovat anotace virtuálním strojem Java za běhu a číst pomocí reflexe . Z existujících v Javě je možné vytvářet metaanotace.

Dějiny

Platforma Java má různé ad-hoc anotační mechanismy - například transientmodifikátor nebo @deprecatedznačku javadoc. Java Specification Request JSR-175 představil anotace všeobecné použití (také známý jako metadata ) zařízení na Java Community Process v roce 2002; získala schválení v září 2004. Anotace byly k dispozici v samotném jazyce počínaje verzí 1.5 Java Development Kit (JDK). aptNástroj poskytuje prozatímní rozhraní pro kompilaci čase zpracování anotací v JDK verze 1.5; JSR-269 to formalizovalo a stalo se integrováno do kompilátoru javac ve verzi 1.6.

Integrované poznámky

Java definuje sadu anotací zabudovaných do jazyka. Ze sedmi standardních anotací jsou tři součástí java.lang a zbývající čtyři jsou importovány z java.lang.annotation.

Poznámky aplikované na kód Java:

  • @Override- Zkontroluje, zda je metoda přepsána . Pokud metoda není nalezena v jedné z nadřazených tříd nebo implementovaných rozhraní, způsobí chybu kompilace .
  • @Deprecated- Označí metodu jako zastaralou. Pokud je použita metoda, způsobí upozornění na kompilaci.
  • @SuppressWarnings- Instruuje kompilátor, aby potlačil časová upozornění kompilace uvedená v parametrech anotace.

Poznámky aplikované na jiné poznámky (známé také jako „Meta poznámky“):

  • @Retention - Určuje, jak je označená anotace uložena, ať už pouze v kódu, zkompilovaná do třídy nebo dostupná za běhu prostřednictvím reflexe.
  • @Documented - Označí další anotaci pro zahrnutí do dokumentace.
  • @Target - Označí jinou anotaci, aby omezil, na jaký druh prvků Java lze anotaci použít.
  • @Inherited - Označí další anotaci, která má být zděděna, do podtříd třídy s anotovanou třídou (implicitně se anotace nedědí podtřídami).

Od verze Java 7 byly do jazyka přidány tři další poznámky.

  • @SafeVarargs- potlačení upozornění pro všechny volající o způsobu nebo montážního závodu s generik varargs parametru, od Javy 7.
  • @FunctionalInterface - Určuje, že deklarace typu má být funkční rozhraní , protože Java 8.
  • @Repeatable - Určuje, že poznámka může být použita více než jednou na stejnou deklaraci, protože Java 8.

Příklad

Integrované poznámky

Tento příklad ukazuje použití @Overrideanotace. Dává instrukci kompilátoru, aby zkontroloval nadřazené třídy pro metody shody. V tomto případě se vygeneruje chyba, protože gettype()metoda třídy Cat ve skutečnosti nepřepíše getType()třídu Animal like, protože se jedná o případ nesouladu . Pokud by @Overrideanotace chyběla, vytvořila by se nová metoda jména gettype()ve třídě Cat.

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";
    }
}

Vlastní anotace

Deklarace typu anotace jsou podobné deklarací normálního rozhraní. Zavináč (@) předchází klíčové slovo rozhraní . Každá deklarace metody definuje prvek typu anotace. Deklarace metody nesmí obsahovat žádné parametry ani klauzuli vyvolání. Návratové typy jsou omezeny na primitiva , String , Class, enums , anotace a pole předchozích typů. Metody mohou mít výchozí hodnoty .

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

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

Anotace mohou zahrnovat volitelný seznam párů klíč – hodnota:

  // 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();
  }

Samotné poznámky mohou být opatřeny poznámkami, které označují, kde a kdy je lze použít:

  @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 {
  }

Kompilátor rezervuje sadu speciálních anotací (včetně @Deprecated, @Overridea @SuppressWarnings) pro syntaktických účelům.

Anotace jsou rozhraními často používány jako způsob pohodlného použití chování na uživatelem definované třídy a metody, které musí být jinak deklarovány v externím zdroji (například v konfiguračním souboru XML) nebo programově (s voláními API). Následuje například anotovaná datová třída JPA :

@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;
    }
}

Anotace nejsou volání metod a samy o sobě nic neudělají. Místo toho je objekt třídy předán implementaci JPA za běhu , která poté extrahuje anotace a vygeneruje relační mapování objektu .

Úplný příklad je uveden níže:

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);
        }
    }
}

zpracovává se

Když je kompilován zdrojový kód Java, poznámky lze zpracovat pomocí modulů plug-in kompilátoru nazývaných procesory poznámek. Procesory mohou vytvářet informační zprávy nebo vytvářet další zdrojové soubory nebo zdroje Java, které lze zase kompilovat a zpracovat. Procesory anotací však nemohou upravovat samotný anotovaný kód. (Kód modifikace mohou být prováděny za použití metod mimo specifikaci jazyka Java.) Java kompilátor podmíněně ukládá anotace metadata v souborech třídy, v případě, že anotace má RetentionPolicyna CLASSnebo RUNTIME. Později může JVM nebo jiné programy hledat metadata, aby určily, jak interagovat s prvky programu nebo změnit jejich chování.

Kromě zpracování anotace pomocí anotačního procesoru může programátor jazyka Java napsat vlastní kód, který ke zpracování anotace používá odrazy. Java SE 5 podporuje nové rozhraní, které je definováno v java.lang.reflectbalíčku. Tento balíček obsahuje rozhraní s názvem AnnotatedElement, který je implementován pomocí odrazu třídách včetně Java Class, Constructor, Field, Methoda Package. Implementace tohoto rozhraní se používají k reprezentaci anotovaného prvku programu, který je aktuálně spuštěn ve virtuálním stroji Java. Toto rozhraní umožňuje číst poznámky reflexivně.

AnnotatedElementRozhraní poskytuje přístup k anotace mají RUNTIMEretenci. Tento přístup je zajištěna pomocí getAnnotation, getAnnotationsa isAnnotationPresentmetod. Protože typy anotací jsou kompilovány a ukládány v souborech bajtového kódu stejně jako třídy, lze anotace vrácené těmito metodami dotazovat stejně jako jakýkoli běžný objekt Java. Níže je uveden kompletní příklad zpracování anotace:

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();
        }
    }
}

Použití ve volné přírodě

Vědci zkoumali použití anotací Java na více než 1094 významných open-source projektech Java hostovaných na GitHubu. Zjistili, že anotace se aktivně udržují, přičemž se přidává mnoho anotací, ale také se mění nebo odstraní kvůli chybám v typu anotace nebo hodnotách. Celkově tato studie zjistila, že existuje malý, ale významný vztah mezi použitím anotací a náchylností k chybám kódu: Java kód s anotacemi má tendenci být méně náchylný k chybám.

Viz také

Reference

externí odkazy