Szukaj…


Wprowadzenie

W Javie adnotacja jest formą metadanych syntaktycznych, które można dodać do kodu źródłowego Java. Dostarcza dane o programie, który nie jest częścią samego programu. Adnotacje nie mają bezpośredniego wpływu na działanie kodu, który adnotują. Klasy, metody, zmienne, parametry i pakiety mogą być opatrzone adnotacjami.

Składnia

  • @AnnotationName // 'Adnotacja znacznika' (bez parametrów)
  • @AnnotationName (someValue) // ustawia parametr o nazwie „wartość”
  • @AnnotacjaNazwa (parametr1 = wartość1) // nazwany parametr
  • @AnnotacjaNazwa (parametr1 = wartość1, parametr2 = wartość2) // wiele nazwanych parametrów
  • @ AnnotationName (param1 = {1, 2, 3}) // nazwany parametr tablicy
  • @AnnotacjaNazwa ({wartość1}) // tablica z pojedynczym elementem jako parametrem o nazwie „wartość”

Uwagi

Rodzaje parametrów

Dozwolone są tylko stałe wyrażenia następujących typów dla parametrów, a także tablice tych typów:

  • String
  • Class
  • typy prymitywne
  • Typy wyliczeń
  • Typy adnotacji

Wbudowane adnotacje

Standardowa edycja Java zawiera wstępnie zdefiniowane adnotacje. Nie musisz ich definiować samodzielnie i możesz z nich korzystać natychmiast. Pozwalają one kompilatorowi na pewne podstawowe sprawdzanie metod, klas i kodu.

@Nadpisanie

Ta adnotacja dotyczy metody i mówi, że ta metoda musi zastąpić metodę „nadklasy” lub zaimplementować definicję metody abstrakcyjnej nadklasy. Jeśli ta adnotacja zostanie użyta z jakąkolwiek inną metodą, kompilator zgłosi błąd.

Betonowa nadklasa

public class Vehicle {
   public void drive() {
        System.out.println("I am driving");
   }
}

class Car extends Vehicle {
    // Fine
    @Override
    public void drive() {
        System.out.prinln("Brrrm, brrm");
    }
}

Klasa abstrakcyjna

abstract class Animal  {
   public abstract void makeNoise(); 
}

class Dog extends Animal {
    // Fine
    @Override
    public void makeNoise() {
        System.out.prinln("Woof");
    }
}

Nie działa

class Logger1 {
    public void log(String logString) {
        System.out.prinln(logString);
    }
}

class Logger2 {
    // This will throw compile-time error. Logger2 is not a subclass of Logger1. 
    // log method is not overriding anything
    @Override
    public void log(String logString) {
        System.out.println("Log 2" + logString);
    }
}

Głównym celem jest wyłapanie pomyłki, gdy myślisz, że przesłoniłeś metodę, ale w rzeczywistości definiujesz nową.

class Vehicle {
   public void drive() {
        System.out.println("I am driving");
   }
}

class Car extends Vehicle {
    // Compiler error. "dirve" is not the correct method name to override.
    @Override
    public void dirve() {
        System.out.prinln("Brrrm, brrm");
    }
}

Pamiętaj, że znaczenie @Override zmieniało się z czasem:

  • W Javie 5 oznaczało to, że metoda z adnotacjami musiała zastąpić nieabstrakcyjną metodę zadeklarowaną w łańcuchu nadklasy.
  • Począwszy od języka Java 6, jest również spełnione, czy metoda z adnotacjami implementuje metodę abstrakcyjną zadeklarowaną w hierarchii nadklasy / interfejsu klas.

(Może to czasami powodować problemy podczas cofania kodu do Java 5.)

@Deprecated

Oznacza to, że metoda jest przestarzała. Może być tego kilka przyczyn:

  • interfejs API jest wadliwy i niepraktyczny do naprawienia,

  • korzystanie z interfejsu API może prowadzić do błędów,

  • interfejs API został zastąpiony przez inny interfejs API,

  • interfejs API jest przestarzały,

  • interfejs API jest eksperymentalny i podlega niezgodnym zmianom,

  • lub dowolna kombinacja powyższych.

Konkretny powód wycofania zwykle można znaleźć w dokumentacji interfejsu API.


Adnotacja spowoduje, że kompilator wyśle błąd, jeśli go użyjesz. IDE mogą również podkreślać tę metodę jako przestarzałą

class ComplexAlgorithm {
    @Deprecated
    public void oldSlowUnthreadSafeMethod() {
        // stuff here
    }
    
    public void quickThreadSafeMethod() {
        // client code should use this instead
    }
}

@Tłumić ostrzeżenia

W prawie wszystkich przypadkach, gdy kompilator emituje ostrzeżenie, najodpowiedniejszym działaniem jest usunięcie przyczyny. W niektórych przypadkach (na przykład kod generyczny wykorzystujący kod pre-generyczny bezpieczny dla typu), może to nie być możliwe i lepiej jest ukryć te ostrzeżenia, których się spodziewasz i których nie można naprawić, aby lepiej widzieć nieoczekiwane ostrzeżenia.

Ta adnotacja może być zastosowana do całej klasy, metody lub linii. Jako parametr przyjmuje kategorię ostrzeżenia.

@SuppressWarnings("deprecation")
public class RiddledWithWarnings {
    // several methods calling deprecated code here
}

@SuppressWarning("finally")
public boolean checkData() {
    // method calling return from within finally block
}

Lepiej ograniczyć zakres adnotacji w jak największym stopniu, aby zapobiec również tłumieniu nieoczekiwanych ostrzeżeń. Na przykład ograniczenie zakresu adnotacji do pojedynczej linii:

ComplexAlgorithm algorithm = new ComplexAlgorithm();
@SuppressWarnings("deprecation") algoritm.slowUnthreadSafeMethod(); 
// we marked this method deprecated in an example above

@SuppressWarnings("unsafe") List<Integer> list = getUntypeSafeList(); 
// old library returns, non-generic List containing only integers

Ostrzeżenia obsługiwane przez tę adnotację mogą się różnić w zależności od kompilatora. Tylko unchecked i ostrzeżenia o deprecation są wyraźnie wymienione w JLS. Nierozpoznane typy ostrzeżeń zostaną zignorowane.

@SafeVarargs

Z powodu kasowania typu void method(T... t) zostanie przekonwertowana na void method(Object[] t) co oznacza, że kompilator nie zawsze jest w stanie sprawdzić, czy użycie varargs jest bezpieczne dla danego typu. Na przykład:

private static <T> void generatesVarargsWarning(T... lists) {

Istnieją przypadki, w których użycie jest bezpieczne, w którym to przypadku można SafeVarargs komentarz adnotacją SafeVarargs aby wyłączyć ostrzeżenie. To oczywiście ukrywa ostrzeżenie, jeśli twoje użycie jest również niebezpieczne.

@Funkcjonalny interfejs

Jest to opcjonalna adnotacja używana do oznaczenia interfejsu FunctionalInterface. Spowoduje, że kompilator narzeka, jeśli nie jest zgodny ze specyfikacją FunctionalInterface (ma jedną abstrakcyjną metodę)

@FunctionalInterface
public interface ITrade {
  public boolean check(Trade t);
}

@FunctionalInterface
public interface Predicate<T> {
  boolean test(T t);
}

Adnotacja w środowisku wykonawczym sprawdza się poprzez odbicie

Java Reflection API pozwala programiście przeprowadzać różne kontrole i operacje na polach klas, metodach i adnotacjach w czasie wykonywania. Jednak aby adnotacja była w ogóle widoczna w czasie wykonywania, RetentionPolicy należy zmienić na RUNTIME , jak pokazano w poniższym przykładzie:

@interface MyDefaultAnnotation {

}

@Retention(RetentionPolicy.RUNTIME)
@interface MyRuntimeVisibleAnnotation {

}

public class AnnotationAtRuntimeTest {

    @MyDefaultAnnotation
    static class RuntimeCheck1 {
    }
    
    @MyRuntimeVisibleAnnotation
    static class RuntimeCheck2 {
    }
    
    public static void main(String[] args) {
        Annotation[] annotationsByType = RuntimeCheck1.class.getAnnotations();
        Annotation[] annotationsByType2 = RuntimeCheck2.class.getAnnotations();

        System.out.println("default retention: " + Arrays.toString(annotationsByType));
        System.out.println("runtime retention: " + Arrays.toString(annotationsByType2));
    }
}

Definiowanie typów adnotacji

Typy adnotacji są definiowane za pomocą @interface . Parametry są zdefiniowane podobnie do metod zwykłego interfejsu.

@interface MyAnnotation {
    String param1();
    boolean param2();
    int[] param3();  // array parameter 
}

Wartości domyślne

@interface MyAnnotation {
    String param1() default "someValue";
    boolean param2() default true;
    int[] param3() default {};
}

Meta-adnotacje

Meta-adnotacje to adnotacje, które można zastosować do typów adnotacji. Specjalne predefiniowane meta-adnotacje określają, w jaki sposób można używać typów adnotacji.

@Cel

Meta-adnotacja @Target ogranicza typy, do których można zastosować adnotację.

@Target(ElementType.METHOD)
@interface MyAnnotation {
    // this annotation can only be applied to methods
}

Za pomocą notacji tablicowej można dodać wiele wartości, np. @Target({ElementType.FIELD, ElementType.TYPE})

Dostępne wartości

ElementType cel przykładowe użycie elementu docelowego
ANNOTATION_TYPE typy adnotacji
@Retention(RetentionPolicy.RUNTIME) 
@interface MyAnnotation
KONSTRUKTOR konstruktory
@MyAnnotation
public MyClass() {}
POLE pola, stałe wyliczeniowe
@XmlAttribute
private int count;
LOCAL_VARIABLE deklaracje zmiennych wewnątrz metod
for (@LoopVariable int i = 0; i < 100; i++) {
@Unused
String resultVariable;
}
PAKIET pakiet (w package-info.java )
@Deprecated
package very.old;
METODA metody
@XmlElement
public int getCount() {...}
PARAMETR parametry metody / konstruktora
public Rectangle(
@NamedArg("width") double width,
@NamedArg("height") double height) {
...
}
RODZAJ klasy, interfejsy, wyliczenia
@XmlRootElement
public class Report {}
Java SE 8
ElementType cel przykładowe użycie elementu docelowego
TYPE_PARAMETER Wpisz deklaracje parametrów
public <@MyAnnotation T> void f(T t) {}
TYPE_USE Zastosowanie rodzaju
Object o = "42";
String s = (@MyAnnotation String) o;

@Zatrzymywanie

Meta-adnotacja @Retention określa widoczność adnotacji podczas procesu kompilacji lub wykonywania aplikacji. Domyślnie adnotacje są zawarte w plikach .class , ale nie są widoczne w czasie wykonywania. Aby adnotacja była dostępna w środowisku wykonawczym, należy ustawić RetentionPolicy.RUNTIME dla tej adnotacji.

@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
    // this annotation can be accessed with reflections at runtime
}

Dostępne wartości

Zasady przechowywania Efekt
KLASA Adnotacja jest dostępna w pliku .class , ale nie w czasie wykonywania
RUNTIME Adnotacja jest dostępna w czasie wykonywania i można uzyskać do niej dostęp poprzez odbicie
ŹRÓDŁO Adnotacja jest dostępna w czasie kompilacji, ale nie jest dodawana do plików .class . Adnotacja może być używana np. Przez procesor adnotacji.

@Documented

Meta-adnotacja @Documented służy do oznaczania adnotacji, których użycie powinno być udokumentowane przez generatory dokumentacji API, takie jak javadoc . Nie ma żadnych wartości. W przypadku @Documented wszystkie klasy, które używają adnotacji, wyświetlą ją na swojej stronie dokumentacji. Bez @Documented nie można zobaczyć, które klasy używają adnotacji w dokumentacji.

@Dziedziczny

@Inherited meta-adnotacja ma zastosowanie do adnotacji, które są stosowane do klas. Nie ma żadnych wartości. Oznaczenie adnotacji jako @Inherited zmienia sposób, w jaki działa zapytanie dotyczące adnotacji.

  • W przypadku nie odziedziczonej adnotacji zapytanie sprawdza tylko badaną klasę.
  • W przypadku odziedziczonej adnotacji kwerenda sprawdzi także łańcuch superklasy (rekurencyjnie), dopóki nie zostanie znaleziona instancja adnotacji.

Zauważ, że odpytywane są tylko superklasy: wszelkie adnotacje dołączone do interfejsów w hierarchii klas będą ignorowane.

@ Powtarzalne

Meta-adnotacja @Repeatable została dodana w Javie 8. Wskazuje ona, że do instancji adnotacji można dołączyć wiele instancji adnotacji. Ta meta-adnotacja nie ma wartości.

Pobieranie wartości adnotacji w czasie wykonywania

Możesz pobrać bieżące właściwości adnotacji, używając Reflection, aby pobrać metodę lub pole lub klasę, do której zastosowano adnotację, a następnie pobrać pożądane właściwości.

@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
    String key() default "foo";
    String value() default "bar";
}


class AnnotationExample {
    // Put the Annotation on the method, but leave the defaults
    @MyAnnotation
    public void testDefaults() throws Exception {
        // Using reflection, get the public method "testDefaults", which is this method with no args
        Method method = AnnotationExample.class.getMethod("testDefaults", null);

        // Fetch the Annotation that is of type MyAnnotation from the Method
        MyAnnotation annotation = (MyAnnotation)method.getAnnotation(MyAnnotation.class);

        // Print out the settings of the Annotation
        print(annotation);
    }

    //Put the Annotation on the method, but override the settings
    @MyAnnotation(key="baz", value="buzz")
    public void testValues() throws Exception {
        // Using reflection, get the public method "testValues", which is this method with no args
        Method method = AnnotationExample.class.getMethod("testValues", null);

        // Fetch the Annotation that is of type MyAnnotation from the Method
        MyAnnotation annotation = (MyAnnotation)method.getAnnotation(MyAnnotation.class);

        // Print out the settings of the Annotation
        print(annotation);
    }

    public void print(MyAnnotation annotation) {
        // Fetch the MyAnnotation 'key' & 'value' properties, and print them out 
        System.out.println(annotation.key() + " = " + annotation.value());
    }

    public static void main(String[] args) {
        AnnotationExample example = new AnnotationExample();
        try {
            example.testDefaults();
            example.testValues();
        } catch( Exception e ) {
            // Shouldn't throw any Exceptions
            System.err.println("Exception [" + e.getClass().getName() + "] - " + e.getMessage());
            e.printStackTrace(System.err);
        }
    }
}

Wyjście będzie

foo = bar
baz = buzz

Powtarzające się adnotacje

Do wersji Java 8 dwa wystąpienia tej samej adnotacji nie mogły być zastosowane do jednego elementu. Standardowym obejściem było użycie adnotacji kontenera zawierającej tablicę innych adnotacji:

// Author.java
@Retention(RetentionPolicy.RUNTIME)
public @interface Author {
    String value();
}

// Authors.java
@Retention(RetentionPolicy.RUNTIME)
public @interface Authors {
    Author[] value();
}

// Test.java
@Authors({
    @Author("Mary"),
    @Author("Sam")
})
public class Test {
    public static void main(String[] args) {
        Author[] authors = Test.class.getAnnotation(Authors.class).value();
        for (Author author : authors) {
            System.out.println(author.value());
            // Output:
            // Mary
            // Sam
        }
    }
}
Java SE 8

Java 8 zapewnia czystszy, bardziej przejrzysty sposób korzystania z adnotacji kontenera, z wykorzystaniem adnotacji @Repeatable . Najpierw dodajemy to do klasy Author :

@Repeatable(Authors.class)

Mówi to Javie, aby traktowało wiele adnotacji @Author tak, jakby były otoczone kontenerem @Authors . Możemy również użyć Class.getAnnotationsByType() aby uzyskać dostęp do tablicy @Author według jej własnej klasy, zamiast przez jej kontener:

@Author("Mary")
@Author("Sam")
public class Test {
    public static void main(String[] args) {
        Author[] authors = Test.class.getAnnotationsByType(Author.class);
        for (Author author : authors) {
            System.out.println(author.value());
            // Output:
            // Mary
            // Sam
        }
    }
}

Odziedziczone adnotacje

Domyślnie adnotacje klasowe nie dotyczą typów rozszerzających je. Można to zmienić poprzez dodanie @Inherited adnotacji definicji adnotacji

Przykład

Rozważ następujące 2 adnotacje:

@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface InheritedAnnotationType {
}

i

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

Jeśli trzy klasy mają takie adnotacje:

@UninheritedAnnotationType
class A {
}

@InheritedAnnotationType
class B extends A {
}

class C extends B {
}

uruchamianie tego kodu

System.out.println(new A().getClass().getAnnotation(InheritedAnnotationType.class));
System.out.println(new B().getClass().getAnnotation(InheritedAnnotationType.class));
System.out.println(new C().getClass().getAnnotation(InheritedAnnotationType.class));
System.out.println("_________________________________");
System.out.println(new A().getClass().getAnnotation(UninheritedAnnotationType.class));
System.out.println(new B().getClass().getAnnotation(UninheritedAnnotationType.class));
System.out.println(new C().getClass().getAnnotation(UninheritedAnnotationType.class));

wypisze wynik podobny do tego (w zależności od pakietów adnotacji):

null
@InheritedAnnotationType()
@InheritedAnnotationType()
_________________________________
@UninheritedAnnotationType()
null
null

Zauważ, że adnotacje można dziedziczyć tylko z klas, a nie z interfejsów.

Skompiluj przetwarzanie czasu za pomocą procesora adnotacji

Ten przykład pokazuje, jak wykonać sprawdzanie czasu kompilacji elementu z adnotacjami.

Adnotacja

Adnotacja @Setter to marker, który można zastosować do metod. Adnotacja zostanie odrzucona podczas kompilacji, a później nie będzie dostępna.

package annotation;

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

@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.METHOD)
public @interface Setter {
}

Procesor adnotacji

Klasa SetterProcessor jest używana przez kompilator do przetwarzania adnotacji. Sprawdza, czy metody opatrzone adnotacją @Setterpublic , static metodami o nazwie zaczynającej się od set i posiadającej dużą literę jako 4 literę. Jeśli jeden z tych warunków nie zostanie spełniony, do Messager zostanie zapisany błąd. Kompilator zapisuje to na stderr, ale inne narzędzia mogą wykorzystywać tę informację inaczej. Np. NetBeans IDE pozwala użytkownikowi określić procesory adnotacji, które są używane do wyświetlania komunikatów o błędach w edytorze.

package annotation.processor;

import annotation.Setter;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;

@SupportedAnnotationTypes({"annotation.Setter"})
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class SetterProcessor extends AbstractProcessor {

    private Messager messager;

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        // get elements annotated with the @Setter annotation
        Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(Setter.class);

        for (Element element : annotatedElements) {
            if (element.getKind() == ElementKind.METHOD) {
                // only handle methods as targets
                checkMethod((ExecutableElement) element);
            }
        }

        // don't claim annotations to allow other processors to process them
        return false;
    }

    private void checkMethod(ExecutableElement method) {
        // check for valid name
        String name = method.getSimpleName().toString();
        if (!name.startsWith("set")) {
            printError(method, "setter name must start with \"set\"");
        } else if (name.length() == 3) {
            printError(method, "the method name must contain more than just \"set\"");
        } else if (Character.isLowerCase(name.charAt(3))) {
            if (method.getParameters().size() != 1) {
                printError(method, "character following \"set\" must be upper case");
            }
        }

        // check, if setter is public
        if (!method.getModifiers().contains(Modifier.PUBLIC)) {
            printError(method, "setter must be public");
        }

        // check, if method is static
        if (method.getModifiers().contains(Modifier.STATIC)) {
            printError(method, "setter must not be static");
        }
    }

    private void printError(Element element, String message) {
        messager.printMessage(Diagnostic.Kind.ERROR, message, element);
    }

    @Override
    public void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);

        // get messager for printing errors
        messager = processingEnvironment.getMessager();
    }

}

Opakowanie

Aby zastosować kompilator, procesor adnotacji musi zostać udostępniony SPI (patrz ServiceLoader ).

W tym celu plik jar META-INF/services/javax.annotation.processing.Processor musi zostać dodany do pliku jar zawierającego procesor adnotacji i adnotację oprócz innych plików. Plik musi zawierać pełną nazwę procesora adnotacji, tzn. Powinien wyglądać tak

annotation.processor.SetterProcessor

Zakładamy, że plik jar nazywa się poniżej AnnotationProcessor.jar .

Przykładowa klasa z adnotacjami

Następująca klasa to przykładowa klasa w pakiecie domyślnym z adnotacjami stosowanymi do poprawnych elementów zgodnie z zasadami przechowywania. Jednak tylko procesor adnotacji uważa drugą metodę za prawidłowy cel adnotacji.

import annotation.Setter;

public class AnnotationProcessorTest {
    
    @Setter
    private void setValue(String value) {}

    @Setter
    public void setString(String value) {}
    
    @Setter
    public static void main(String[] args) {}
    
}

Korzystanie z procesora adnotacji z javac

Jeśli procesor adnotacji zostanie wykryty przy użyciu interfejsu SPI, jest automatycznie używany do przetwarzania elementów z adnotacjami. Np. Kompilacja klasy AnnotationProcessorTest przy użyciu

javac -cp AnnotationProcessor.jar AnnotationProcessorTest.java

daje następujący wynik

AnnotationProcessorTest.java:6: error: setter must be public
    private void setValue(String value) {}
                 ^
AnnotationProcessorTest.java:12: error: setter name must start with "set"
    public static void main(String[] args) {}
                       ^
2 errors

zamiast kompilować normalnie. Nie utworzono pliku .class .

Można temu zapobiec, określając opcję -proc:none dla javac . Możesz także zrezygnować ze zwykłej kompilacji, podając -proc:only zamiast tego.

Integracja IDE

Netbeans

Procesory adnotacji mogą być używane w edytorze NetBeans. Aby to zrobić, procesor adnotacji musi zostać określony w ustawieniach projektu:

  1. przejdź do Project Properties > Build > Compiling

  2. dodaj znaczniki Enable Annotation Processing i Enable Annotation Processing in Editor

  3. kliknij Add obok listy procesorów adnotacji

  4. w wyskakującym okienku wpisz pełną nazwę klasy procesora adnotacji i kliknij Ok .

Wynik

Okno edytora z niestandardowym komunikatem o błędzie

Idea Adnotacji

Specyfikacja języka Java opisuje adnotacje w następujący sposób:

Adnotacja jest znacznikiem, który wiąże informacje z konstrukcją programu, ale nie ma żadnego wpływu w czasie wykonywania.

Adnotacje mogą pojawiać się przed typami lub deklaracjami. Mogą pojawić się w miejscu, w którym mogą ubiegać się zarówno o typ, jak i deklarację.
To, czego dokładnie dotyczy adnotacja, jest regulowane przez „meta-adnotację” @Target . Aby uzyskać więcej informacji, zobacz „Definiowanie typów adnotacji” .

Adnotacje są używane do wielu celów. Frameworki, takie jak Spring i Spring-MVC, wykorzystują adnotacje, aby określić, gdzie należy wstrzykiwać zależności lub gdzie należy kierować żądania.

Inne frameworki używają adnotacji do generowania kodu. Lombok i JPA to najlepsze przykłady, które wykorzystują adnotacje do generowania kodu Java (i SQL).

Ten temat ma na celu zapewnienie kompleksowego przeglądu:

  • Jak zdefiniować własne adnotacje?

  • Jakie adnotacje zapewnia język Java?

  • Jak adnotacje są używane w praktyce?

Adnotacje dla „tego” i parametrów odbiornika

Kiedy adnotacje Java zostały wprowadzone po raz pierwszy, nie było przepisu pozwalającego na adnotację celu metody instancji lub parametru ukrytego konstruktora dla konstruktora klas wewnętrznych. Zostało to naprawione w Javie 8 poprzez dodanie deklaracji parametrów odbiornika ; patrz JLS 8.4.1 .

Parametr odbiornika jest opcjonalnym urządzeniem syntaktycznym dla metody instancji lub konstruktora klasy wewnętrznej. W przypadku metody instancji parametr odbierający reprezentuje obiekt, dla którego metoda jest wywoływana. W przypadku konstruktora klasy wewnętrznej parametr odbierający reprezentuje bezpośrednio otaczającą instancję nowo zbudowanego obiektu. Tak czy inaczej, parametr odbiornika istnieje wyłącznie po to, aby umożliwić oznaczenie typu reprezentowanego obiektu w kodzie źródłowym, tak aby ten typ mógł zostać opatrzony adnotacjami. Parametr odbiornika nie jest parametrem formalnym; a dokładniej, nie jest to deklaracja jakiejkolwiek zmiennej (§4.12.3), nigdy nie jest związana z żadną wartością przekazywaną jako argument w wyrażeniu wywołania metody lub wyrażeniu tworzenia instancji klasy kwalifikowanej i nie ma żadnego wpływu na czas pracy.

Poniższy przykład ilustruje składnię dla obu rodzajów parametrów odbiornika:

public class Outer {
    public class Inner {
        public Inner (Outer this) {
           // ...
        }
        public void doIt(Inner this) {
           // ...
        }
    }
}

Jedynym celem parametrów odbiornika jest umożliwienie dodawania adnotacji. Na przykład możesz mieć niestandardową adnotację @IsOpen której celem jest stwierdzenie, że obiekt Closeable nie został zamknięty podczas wywołania metody. Na przykład:

public class MyResource extends Closeable {
    public void update(@IsOpen MyResource this, int value) {
        // ...
    }

    public void close() {
        // ...
    }
}

Na jednym poziomie adnotacja @IsOpen na this może po prostu służyć jako dokumentacja. Moglibyśmy jednak potencjalnie zrobić więcej. Na przykład:

  • Procesor adnotacji mógł włożyć czek runtime, że this nie jest w stanie zamkniętym, gdy update jest tzw.
  • Sprawdzania kod może wykonywać statycznej analizy kodu, aby znaleźć przypadki, gdzie this może być zamknięty, gdy update jest tzw.

Dodaj wiele wartości adnotacji

Parametr adnotacji może przyjmować wiele wartości, jeśli jest zdefiniowany jako tablica. Na przykład standardowa adnotacja @SuppressWarnings jest zdefiniowana w następujący sposób:

public @interface SuppressWarnings {
    String[] value();
}

Parametr value jest tablicą ciągów. Możesz ustawić wiele wartości, używając notacji podobnej do inicjatorów Array:

@SuppressWarnings({"unused"})
@SuppressWarnings({"unused", "javadoc"})

Jeśli musisz ustawić tylko jedną wartość, nawiasy można pominąć:

@SuppressWarnings("unused") 


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow