GetAnnotation: примеры (JAVA)
getAnnotation(Class annotationClass): T Общее описание метода getAnnotation
Метод getAnnotation служит для получения экземпляра аннотации, связанной с элементом отражения (элемент реализует интерфейс java.lang.reflect.AnnotatedElement). На практике этот метод доступен через классы Class, Method, Field, Constructor, Parameter и другие реализующие AnnotatedElement.
Подпись (основные варианты):
<A extends java.lang.annotation.Annotation> A getAnnotation(Class<A> annotationClass)
Аргументы:
- annotationClass - класс аннотации (Class<A>). Если
null, будет выброшено java.lang.NullPointerException.
Возвращаемое значение:
- Экземпляр аннотации типа
A, если аннотация находится на элементе (включая случаи наследования для классов, помеченных @Inherited, когда вызывается на Class). null, если аннотация не найдена или имеет RetentionPolicy отличное от RUNTIME (например, CLASS или SOURCE).
Особенности поведения:
- Для классов поиск может учитывать наследование аннотаций, помеченных @Inherited: Class.getAnnotation вернет аннотацию от суперкласса, если она отмечена @Inherited и отсутствует в текущем классе. Для методов, полей и параметров наследование аннотаций автоматически не выполняется.
- Для повторяемых аннотаций (repeatable) getAnnotation возвращает только одну аннотацию указанного типа. Для получения всех экземпляров используется getAnnotationsByType.
- Если аннотация имеет RetentionPolicy.RUNTIME, она доступна через рефлексию. При политике CLASS или SOURCE данные аннотации недоступны во время выполнения.
- getAnnotation не создает новых классов аннотаций вручную: возвращается прокси-объект, реализующий интерфейс аннотации, с доступом к значениям полей аннотации.
Связанные методы, влияющие на поведение:
- getAnnotations() - возвращает все аннотации, включая унаследованные для классов.
- getDeclaredAnnotation(Class<A>) - возвращает аннотацию, непосредственно объявленную на элементе (без учета наследования).
- getAnnotationsByType(Class<A>) - учитывает контейнер для repeatable-annotations и возвращает массив всех вхождений.
Короткие примеры применения
Пример 1: простой класс с аннотацией RUNTIME
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@interface Info { String value(); }
@Info("DemoClass")
class Demo {}
public class Test1 {
public static void main(String[] args) {
Info info = Demo.class.getAnnotation(Info.class);
System.out.println(info);
System.out.println(info != null ? info.value() : "null");
}
}
@Info(value=DemoClass) DemoClass
Пример 2: аннотация с RetentionPolicy.CLASS недоступна во время выполнения
import java.lang.annotation.*;
@Retention(RetentionPolicy.CLASS)
@interface Hidden { }
@Hidden
class C {}
public class Test2 {
public static void main(String[] args) {
Object a = C.class.getAnnotation(Hidden.class);
System.out.println(a);
}
}
null
Пример 3: отличие getAnnotation и getDeclaredAnnotation с @Inherited
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface Mark { }
@Mark
class Parent {}
class Child extends Parent {}
public class Test3 {
public static void main(String[] args) {
System.out.println(Child.class.getAnnotation(Mark.class));
System.out.println(Child.class.getDeclaredAnnotation(Mark.class));
}
}
@Mark() null
Пример 4: repeatable-аннотация и getAnnotationsByType
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Tags.class)
@interface Tag { String value(); }
@Retention(RetentionPolicy.RUNTIME)
@interface Tags { Tag[] value(); }
@Tag("a")
@Tag("b")
class X {}
public class Test4 {
public static void main(String[] args) {
Tag t = X.class.getAnnotation(Tag.class);
Tag[] all = X.class.getAnnotationsByType(Tag.class);
System.out.println(t);
System.out.println(all.length + ": " + java.util.Arrays.toString(all));
}
}
@Tag(value=a) 2: [@Tag(value=a), @Tag(value=b)]
Пример 5: метод-аннотация не наследуется при переопределении
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@interface MAnn { String v(); }
class Base {
@MAnn(v="base")
public void m() {}
}
class Sub extends Base {
@Override
public void m() {}
}
public class Test5 {
public static void main(String[] args) throws Exception {
System.out.println(Sub.class.getMethod("m").getAnnotation(MAnn.class));
}
}
null
Похожие методы в Java и их особенности
- getDeclaredAnnotation(Class<A>) - возвращает аннотацию, объявленную непосредственно на элементе; не учитывает наследование. Предпочтителен при желании избежать унаследованных меток.
- getAnnotations() - возвращает массив всех аннотаций, видимых для элемента (для классов включает унаследованные аннотации). Удобен при перечислении всех меток.
- getDeclaredAnnotations() - возвращает все аннотации, явно объявленные на элементе.
- getAnnotationsByType(Class<A>) - возвращает все вхождения повторяемой аннотации, распаковывая контейнер. Предпочтителен для repeatable-аннотаций.
- isAnnotationPresent(Class<? extends Annotation>) - булевый индикатор наличия аннотации; эквивалент проверки getAnnotation(...) != null.
Выбор между ними зависит от цели: получение единственного экземпляра (getAnnotation), учет контейнера repeatable (getAnnotationsByType) или строгое чтение только локально объявленных аннотаций (getDeclaredAnnotation / getDeclaredAnnotations).
Аналоги в других языках и отличия
Краткие соответствия и отличия по языкам:
- C#: System.Reflection.Attribute.GetCustomAttribute / GetCustomAttributes. Возвращает объект Attribute; поведение похоже, но атрибуты доступны по умолчанию при компиляции, механизм наследования управляется атрибутом InheritedAttribute. Пример:
[System.AttributeUsage(System.AttributeTargets.Class, Inherited=true)] class MyAttr : System.Attribute { public string V; public MyAttr(string v){V=v;} } [MyAttr("A")] class P {} class C : P {} // получение var a = (MyAttr)typeof(C).GetCustomAttribute(typeof(MyAttr)); Console.WriteLine(a.V);A
- Kotlin: kotlin.reflect.KAnnotatedElement.findAnnotation / annotations. Поведение схоже с Java, но добавлена более удобная интеграция с Kotlin-метаданными. Пример:
@Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.RUNTIME) annotation class Info(val v: String) @Info("K") class A println(A::class.findAnnotation()?.v) K
- Python: в языке аннотации типов и декораторы используются иначе. Для получения метаданных декоратор обычно сохраняет данные в атрибуте функции. Пример:
def tag(v): def d(f): f._tag = v return f return d @tag('x') def f(): pass print(getattr(f, '_tag', None))x
- JavaScript / TypeScript: при использовании reflect-metadata доступно Reflect.getMetadata(metaKey, target, propertyKey). Аннотации реализуются как метаданные; нет встроенного механизма стандартной аннотации как в Java. Пример (TypeScript + reflect-metadata):
import 'reflect-metadata'; function Tag(v: string){ return (t:any, k?:string)=> Reflect.defineMetadata('tag', v, t, k); } class A{ @Tag('x') m(){} } console.log(Reflect.getMetadata('tag', A.prototype, 'm'))x
- PHP (8+): ReflectionAttribute / getAttributes. В PHP 8+ есть native attributes; поведение похоже по назначению, синтаксис иной. Пример:
#[Attribute] class Info { public function __construct(public string $v) {} } #[Info('x')] class A {} $ref = new ReflectionClass(A::class); $attr = $ref->getAttributes(Info::class)[0]->newInstance(); echo $attr->v;x
- Go: рефлексия использует теги struct field (reflect.StructTag). Аннотации как в Java отсутствуют; метаданные хранятся в строковых тегах полей. Пример:
type T struct { X int `json:"x" tag:"v"` } import "reflect" var t T fmt.Println(reflect.TypeOf(t).Field(0).Tag.Get("tag"))v
- SQL, Lua: встроенного механизма аннотаций нет; обычно используются дополнительные схемы, комментарии или внешние метаданные.
В отличие от Java, в ряде языков аннотации реализуются через отдельные библиотеки или имеют иной семантический уровень (например, теги в Go или атрибуты в C#), что влияет на доступность во время выполнения и на поддержку наследования.
Типичные ошибки и их проявления
- NullPointerException при null аргументе. Вызов getAnnotation(null) вызывает NPE.
Demo.class.getAnnotation(null);Exception in thread "main" java.lang.NullPointerException at java.base/java.lang.Class.getAnnotation(Class.java:...) - Ожидание аннотации с RetentionPolicy.CLASS. При политике CLASS аннотация недоступна в рантайме, результат null.
@Retention(RetentionPolicy.CLASS) @interface A{} System.out.println(X.class.getAnnotation(A.class));null
- Непонимание наследования для методов. Для методов getAnnotation не ищет аннотацию в переопределенных версиях автоматически: часто выводится null, если аннотация стоит в базовом методе, а вызов производится на методе из подкласса.
- Неправильная обработка repeatable-аннотаций. Ожидание, что getAnnotation вернет массив; на самом деле нужен getAnnotationsByType. Пример:
@Tag("a") @Tag("b") class A{} System.out.println(A.class.getAnnotation(Tag.class)); System.out.println(Arrays.toString(A.class.getAnnotationsByType(Tag.class)));@Tag(value=a) [@Tag(value=a), @Tag(value=b)]
Изменения и история
- Java 8 ввела поддержку repeatable аннотаций и связанные методы getAnnotationsByType и getDeclaredAnnotationsByType, что упростило работу с множественными аннотациями одного типа.
- Ранее методы getAnnotation, getAnnotations и getDeclaredAnnotations существовали в JDK, однако улучшенная поддержка repeatable-аннотаций появилась именно с Java 8.
- В остальном сигнатуры и семантика getAnnotation оставались стабильными в современных версиях JDK; изменений, ломающих обратную совместимость, не отмечено.
Расширенные примеры и редкие сценарии
Пример: утилита для поиска аннотации метода в иерархии классов и интерфейсов. Полезно при попытке обнаружить аннотацию объявленную в интерфейсе или базовом классе.
import java.lang.annotation.*;
import java.lang.reflect.*;
@Retention(RetentionPolicy.RUNTIME)
@interface A { String v(); }
interface I { @A(v="iface") void m(); }
class B implements I { public void m(){} }
public class Finder {
static A findMethodAnnotation(Method m, Class<A> annClass) {
A a = m.getAnnotation(annClass);
if (a != null) return a;
// поиск в интерфейсах
for (Class<?> inf : m.getDeclaringClass().getInterfaces()) {
try {
Method im = inf.getMethod(m.getName(), m.getParameterTypes());
a = im.getAnnotation(annClass);
if (a != null) return a;
} catch (NoSuchMethodException ignored) {}
}
// поиск в суперклассах
Class<?> sc = m.getDeclaringClass().getSuperclass();
while (sc != null) {
try {
Method sm = sc.getMethod(m.getName(), m.getParameterTypes());
a = sm.getAnnotation(annClass);
if (a != null) return a;
} catch (NoSuchMethodException ignored) {}
sc = sc.getSuperclass();
}
return null;
}
public static void main(String[] args) throws Exception {
Method m = B.class.getMethod("m");
System.out.println(findMethodAnnotation(m, A.class));
}
}
@A(v=iface)
Пояснение: стандартный getAnnotation у метода B.m возвращает null, поскольку аннотация определена в интерфейсе. Представленная логика проходит интерфейсы и суперклассы для нахождения аннотации.
Пример: чтение значений полей аннотации и обработка значений по умолчанию
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@interface Conf { String url() default "localhost"; int port() default 80; }
@Conf(port=8080)
class S {}
public class ReadVals {
public static void main(String[] args) {
Conf c = S.class.getAnnotation(Conf.class);
System.out.println(c.url());
System.out.println(c.port());
}
}
localhost 8080
Пример: обработка аннотации, примененной к параметру метода
import java.lang.annotation.*;
import java.lang.reflect.*;
@Retention(RetentionPolicy.RUNTIME)
@interface P { String v(); }
class X { void f(@P(v="p1") String s) {} }
public class ParamAnn {
public static void main(String[] args) throws Exception {
Method m = X.class.getDeclaredMethod("f", String.class);
Annotation[][] pa = m.getParameterAnnotations();
for (Annotation[] a : pa) System.out.println(java.util.Arrays.toString(a));
}
}
[@P(v=p1)]
Пример: получение мета-аннотаций (аннотация аннотации)
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@interface Meta { }
@Meta
@Retention(RetentionPolicy.RUNTIME)
@interface A { }
@A
class C {}
public class MetaExample {
public static void main(String[] args) {
A a = C.class.getAnnotation(A.class);
if (a != null) {
Meta m = a.annotationType().getAnnotation(Meta.class);
System.out.println(m);
}
}
}
@Meta()
Пояснение: иногда требуется не только читать саму аннотацию, но и её мета-информацию. Для этого используется annotationType().getAnnotation(...).