An annotation is metadata attached to a declaration or type use. It is not a general-purpose object literal: an annotation type is a special interface whose members describe the permitted metadata.
Declaring Annotation Types #
Annotation members have no parameters or type parameters, and may not declare a
throws clause. A member type is limited to a primitive, String, Class,
an enum type, an annotation type, or a one-dimensional array of one of those
types. Values must be compile-time constants, class literals, enum constants,
annotations, or arrays thereof. null is never an annotation element value.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Retry {
int attempts() default 3;
Class<? extends Throwable>[] retryOn() default {};
}@Target constrains where an annotation may occur; relevant locations include
declarations, type uses, type parameters, modules, and record components. If it
is absent, the annotation may be written in any declaration context, but not in
a type context. @Retention controls whether the compiler discards it
(SOURCE), records it in the class file (CLASS, the default), or records it
for reflection (RUNTIME). @Inherited affects only queries on Class, only
for annotations on classes, and only along the superclass chain. It does not
inherit annotations from interfaces, methods, fields, parameters, or type uses.
Defaults are compiled into the annotation type rather than copied into every use. Changing a default can therefore alter the result observed from an old client class file when it is run with the new annotation type.
Reflection and Access Boundaries #
Class, Method, Field, Constructor, Parameter, Package, and Module
implement AnnotatedElement. The getDeclared* methods inspect annotations
directly present on that element. The non-declared methods can include inherited
class annotations; getAnnotationsByType and
getDeclaredAnnotationsByType also unpack repeatable containers.
Retry[] policies = operation.getAnnotationsByType(Retry.class);Runtime retention makes metadata visible; it does not grant access to a member
or make private state safe to expose. Reflection must still pass Java language
access checks and, for named modules, strong-encapsulation checks. trySetAccessible
can only suppress applicable language checks when the package is opened to the
caller; command-line --add-opens is a deployment decision, not a library
contract. The legacy SecurityManager is deprecated for removal, so security
design must use module boundaries, process permissions, input validation, and
explicit APIs instead of assuming a global reflection policy.
Inspection alone should not be used as an initialization mechanism. Reflective construction and invocation can initialize a class, and annotation member access can require linked types to be resolved. Keep static initialization free of required configuration and externally visible side effects.
Repeatable Annotations #
@Repeatable names a container annotation whose sole required member is an
array of the repeated annotation type. The container must have retention at
least as long as the repeated type and a compatible target.
@Repeatable(Labels.class)
@interface Label { String value(); }
@interface Labels { Label[] value(); }
@Label("read") @Label("write")
final class Permission {}For multiple occurrences, the language models the declaration as an implicitly
declared container; class-file consumers therefore see the same container form
as an explicit @Labels({ ... }) declaration. A single @Label is not
generally byte-for-byte equivalent to a single-element explicit container.
getAnnotation(Label.class) does not unpack a container, whereas
getAnnotationsByType(Label.class) does.
Class Files and Proxy Instances #
Runtime-visible declaration annotations are stored in class-file attributes
such as RuntimeVisibleAnnotations; parameter and type-use annotations have
separate attributes. The compiler validates the annotation grammar and encodes
element values. The VM verifies class-file structure, while core reflection
parses the attributes on demand and exposes the result through
AnnotatedElement.
The reflection implementation represents a returned runtime annotation as an
object implementing the annotation interface, commonly a dynamic proxy in
OpenJDK. Clients must treat that representation as unspecified. Rely on
annotationType(), declared member methods, and the Annotation equality,
hash-code, and string contracts rather than the proxy class or identity.
Comments
Read or comment on GitHub (sign-in required to comment)