Recent from talks
Contribute something
Nothing was collected or created yet.
Java annotation
View on WikipediaIn the Java computer programming language, an annotation is a form of syntactic metadata that can be added to Java source code, like an attribute.[1] Classes, methods, variables, parameters and Java packages may be annotated. Like Javadoc tags, Java annotations can be read from source files. Unlike Javadoc tags, Java annotations can also be embedded in and read from Java class files generated by the Java compiler. This allows annotations to be retained by the Java virtual machine at run-time and read via reflection.[2] It is possible to create meta-annotations out of the existing ones in Java.[3]
History
[edit]The Java platform has various ad-hoc annotation mechanisms—for example, the transient modifier, or the @Deprecated javadoc tag. The Java Specification Request JSR-175 introduced the general-purpose annotation (also known as metadata) facility to the Java Community Process in 2002; it gained approval in September 2004.[4]
Annotations became available in the language itself beginning with version 1.5 of the Java Development Kit (JDK). The apt tool provided a provisional interface for compile-time annotation processing in JDK version 1.5; JSR-269 formalized this, and it became integrated into the javac compiler in version 1.6.
In C++26, C++ added annotations for reflection that are similar to Java annotations.
Built-in annotations
[edit]Java defines a set of annotations that are built into the language. Of the seven standard annotations, three are part of java.lang, and the remaining four are imported from java.lang.annotation.[5][6]
| Annotation | Package | Description |
|---|---|---|
@Deprecated |
java.lang |
Marks the method as obsolete. Causes a compile warning if the method is used. |
@FunctionalInterface |
java.lang |
Marks an interface as intended to be a functional interface. |
@Override |
java.lang |
Marks that the method overrides an ancestor class-defined method. Causes a compilation error if the method is not found in one of the parent classes or implemented interfaces. |
@SafeVarargs |
java.lang |
Suppress warnings for all callers of a method or constructor with a generics varargs parameter, since Java 7. |
@SuppressWarnings |
java.lang |
Instructs the compiler to suppress the compile time warnings specified in the annotation parameters. |
@Documented |
java.lang.annotation |
Marks another annotation for inclusion in the documentation. |
@Inherited |
java.lang.annotation |
Marks another annotation to be inherited to subclasses of annotated class (by default annotations are not inherited by subclasses). |
@Native |
java.lang.annotation |
Marks a field defining a constant value as potentially being referenced from native code. |
@Repeatable |
java.lang.annotation |
Marks another annotation as repeatable. |
@Retention |
java.lang.annotation |
Specifies how the marked annotation is stored, whether in code only, compiled into the class, or available at runtime through reflection. |
@Target |
java.lang.annotation |
Marks another annotation to restrict what kind of Java elements the annotation may be applied to. |
In Jakarta EE (formerly Java Platform, Enterprise Edition), the following annotations also exist in jakarta.annotation (formerly javax.annotation):[7][8]
| Annotation | Package | Description |
|---|---|---|
@Generated |
jakarta.annotation |
Marks source code that has been generated (i.e. not written by a user, or automatically generated by a computer). |
@Resource |
jakarta.annotation |
Marks a class, method, or field as a reference to a resource. |
@Resources |
jakarta.annotation |
Declares reference to resources, as a container for multiple resource declarations. |
@PostConstruct |
jakarta.annotation |
Marks a method to indicate that it must be executed after dependency injection to perform initialization, i.e. the method must be invoked before the class is used. |
@PreDestroy |
jakarta.annotation |
Marks a method as a callback notification to indicate the instance is in the process of being removed by the container, i.e. the method is used to release resources held by the instance. |
@Priority |
jakarta.annotation |
Marks any program element to indicate in what order they should be used. |
@Nonnull |
jakarta.annotation |
Marks any element that cannot be null.
|
@Nullable |
jakarta.annotation |
Marks any element that has the explicit possibility of being null.
|
@RunAs |
jakarta.annotation |
Defines the security role of the application during execution in a Jakarta EE container. |
@RolesAllowed |
jakarta.annotation.security |
Marks a method to specify security roles permitted to access the method. |
@PermitAll |
jakarta.annotation.security |
Marks a method to specify that all security roles may access the method. |
@DenyAll |
jakarta.annotation.security |
Marks a method to specify that no security roles may access the method. |
@DeclareRoles |
jakarta.annotation.security |
Specifies security roles used by the application. |
@DataSourceDefinition |
jakarta.annotation.sql |
Defines a container DataSource that is registered with Java Naming and Directory Interface (JNDI).
|
@DataSourceDefinitions |
jakarta.annotation.sql |
Declares a container DataSource, acting as a container for multiple data source declarations.
|
There was previously an annotation, @ManagedBean, located in jakarta.annotation, which was historically used to declare a Managed Bean which are container managed objects that support a small set of basic services such as resource injection, lifecycle callbacks and interceptors. However, it has been removed.[9][10]
Example
[edit]Built-in annotations
[edit]This example demonstrates the use of the @Override annotation. It instructs the compiler to check parent classes for matching methods. In this case, an error is generated because the gettype() method of class Cat doesn't in fact override getType() of class Animal like is desired, because of the mismatching case. If the @Override annotation were absent, a new method of name gettype() would be created in class 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";
}
}
Custom annotations
[edit]Annotation type declarations are similar to normal interface declarations. An at-sign (@) precedes the keyword "interface".
// @Twizzle is an annotation to method toggle().
@Twizzle
public void toggle() {
}
// Declares the annotation Twizzle.
public @interface Twizzle {
}
Annotations may include a set of key-value pairs, which are modeled as methods of the annotation type. Each method declaration defines an element of the annotation type. Method declarations must not have any parameters or a throws clause. Return types are restricted to primitives, String, Class, enums, annotations, and arrays of the preceding types. Methods can have default values.
// 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();
}
Annotations themselves may be annotated to indicate where and when they can be used:
@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 {}
The compiler reserves a set of special annotations (including @Deprecated, @Override and @SuppressWarnings) for syntactic purposes.
Annotations are often used by frameworks as a way of conveniently applying behaviours to user-defined classes and methods that must otherwise be declared in an external source (such as an XML configuration file) or programmatically (with API calls). The following, for example, is an annotated JPA data class:
@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;
}
}
The annotations are not method calls and will not, by themselves, do anything. Rather, the class object is passed to the JPA implementation at run-time, which then extracts the annotations to generate an object–relational mapping.
A complete example is given below:
package com.acme.proj.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.acme.proj.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.acme.proj.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.acme.proj.annotation.UnderConstruction;
import com.acme.proj.annotation.Unfinished;
import com.acme.proj.annotation.Unfinished.Priority;
import com.acme.proj.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);
}
}
}
Processing
[edit]When Java source code is compiled, annotations can be processed by compiler plug-ins called annotation processors. Processors can produce informational messages or create additional Java source files or resources, which in turn may be compiled and processed. However, annotation processors cannot modify the annotated code itself. (Code modifications may be implemented using methods beyond the Java Language Specification.) The Java compiler conditionally stores annotation metadata in the class files, if the annotation has a RetentionPolicy of CLASS or RUNTIME. Later, the JVM or other programs can look for the metadata to determine how to interact with the program elements or change their behavior.
In addition to processing an annotation using an annotation processor, a Java programmer can write their own code that uses reflection to process the annotation. Java SE 5 supports a new interface that is defined in the java.lang.reflect package. This package contains the interface called AnnotatedElement that is implemented by the Java reflection classes including Class, Constructor, Field, Method, and Package. The implementations of this interface are used to represent an annotated element of the program currently running in the Java Virtual Machine. This interface allows annotations to be read reflectively.
The AnnotatedElement interface provides access to annotations having RUNTIME retention. This access is provided by the getAnnotation, getAnnotations, and isAnnotationPresent methods. Because annotation types are compiled and stored in byte code files just like classes, the annotations returned by these methods can be queried just like any regular Java object. A complete example of processing an annotation is provided below:
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.printf("Developer: %s%n", header.developer());
System.out.printf("Last Modified: %s%n", header.lastModified());
// teamMembers returned as String[]
System.out.print("Team members: ");
for (String member : header.teamMembers()) {
System.out.printf("%s, ", member);
}
System.out.println();
System.out.println("Meaning of Life: %s%n", header.meaningOfLife());
}
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
See also
[edit]- Jakarta Annotations
- CLI Attributes
- Java
- Java virtual machine
- Model-driven architecture
- Python decorators, inspired by Java annotations, which have a similar syntax.
References
[edit]- ^ "Annotations". Sun Microsystems. Archived from the original on 2011-09-25. Retrieved 2011-09-30..
- ^ Sun Microsystems (2005). Java(TM) Language Specification (3rd ed.). Prentice Hall. ISBN 0-321-24678-0..
- ^ Dare Obasanjo (2007). "A COMPARISON OF MICROSOFT'S C# PROGRAMMING LANGUAGE TO SUN MICROSYSTEMS' JAVA PROGRAMMING LANGUAGE: Metadata Annotations". Dare Obasanjo. Archived from the original on 2012-09-19. Retrieved 2012-09-20.
- ^ Coward, Danny (2006-11-02). "JSR 175: A Metadata Facility for the JavaTM Programming Language". Java Community Process. Retrieved 2008-03-05.
- ^ "Predefined Annotation Types". Oracle Corporation. Retrieved 2016-12-17.
- ^ "The Built-In Annotations : Standard Annotations". Retrieved 2016-12-17.
- ^ "Jakarta Annotations API 1.3.5 API". Jakarta EE. Retrieved 2025-08-13.
- ^ "Jakarta Annotations". Jakarta EE. Retrieved 2025-08-13.
- ^ "Jakarta Annotations API 1.3.5 API". Jakarta EE. Retrieved 2025-08-13.
- ^ "Jakarta Annotations". Jakarta EE. Retrieved 2025-08-13.
External links
[edit]- Introduction to Java 6 Annotations at Sun Developer Network Site
- An Introduction to Java Annotations by M. M. Islam Chisty
- Srinivasan, Krishna (August 11, 2007). "Annotations in Java 5.0". JavaBeat. Archived from the original on May 31, 2015.
- Hunt, John (24 Feb 2006). "Of Java Annotations". The Register.
- "How to create and implement custom annotations in Java?". So Many Word. February 15, 2014. Archived from the original on Feb 23, 2014.
- "Java Annotations Tutorial with examples". TutorialsDesk. October 9, 2014.
- Thakor, Vicky (13 October 2015). "Understanding Annotations in Java". Java by examples.
Java annotation
View on Grokipedia@ symbol followed by an annotation type name and optional element-value pairs in parentheses, enabling compile-time processing, code generation, and runtime inspection.[2][3]
Annotations serve multiple purposes, including informing the compiler about intended overrides with @Override, suppressing specific warnings via @SuppressWarnings, and marking elements as deprecated using @Deprecated, all of which are predefined in the java.lang package.[4] They also support meta-annotations like @Retention, which defines whether an annotation is retained at compile-time, in class files, or at runtime, and @Target, which restricts where the annotation can be applied.[5][6] Since Java SE 8, enhancements such as type annotations—applicable to type declarations and uses for stronger type checking—and repeating annotations, enabled by the @Repeatable meta-annotation, have expanded their utility in frameworks and pluggable type systems.[7][8] Custom annotations can be created by defining interfaces extending java.lang.annotation.Annotation, allowing developers to build domain-specific metadata for tools like dependency injection or validation libraries.[1] Overall, annotations promote cleaner, more maintainable code by reducing boilerplate and enabling declarative programming paradigms across the Java ecosystem.[9]
Overview
Definition and Purpose
Java annotations are a form of syntactic metadata that can be attached to various program elements in the Java programming language, such as classes, interfaces, fields, methods, parameters, constructors, and, since Java SE 8, type uses.[1] They provide additional information about these elements without directly altering the program's execution flow, serving as a standardized way to embed descriptive data into source code.[10] The primary purpose of annotations is to enable declarative programming by allowing developers to specify configuration, constraints, or behavioral hints in a concise manner.[11] They offer guidance to compilers for tasks like error detection or warning suppression, support compile-time and deployment-time processing for generating code, XML files, or other artifacts, and facilitate runtime examination through reflection for dynamic behaviors.[10] In frameworks, annotations play a key role in areas such as dependency injection (e.g., in Spring) and validation (e.g., in Jakarta Bean Validation), where they declaratively wire components or enforce rules without invasive code changes.[12] Annotations enhance code readability by making metadata explicit and self-documenting, reducing the need for boilerplate code like extensive XML configurations or manual checks.[1] They promote separation of concerns by distinguishing core logic from configuration or tooling directives, leading to more maintainable and modular applications.[11] Prior to their introduction, developers relied on ad hoc approaches such as marker interfaces (e.g., for type categorization) or informal comments and naming conventions to convey similar metadata, which were less structured and harder to process programmatically.[11]Key Concepts
Java annotations are built upon several foundational concepts that define their structure and behavior. An annotation type is a special form of interface that extends thejava.lang.annotation.Annotation marker interface, declared using the @interface keyword.[13] These types serve as blueprints for creating metadata that can be attached to various program elements, enabling declarative programming without altering the core logic of the code.[1]
Central to annotation types are their elements, which are abstract methods defined within the @interface declaration. These elements represent the configurable attributes of the annotation and can include optional default values specified via the default keyword. The return types for elements are strictly limited to primitives (such as int, boolean, double), String, Class, an enum type, another annotation type, or arrays of these types, ensuring type safety and simplicity in processing.[13] For instance, an element might be declared as String value() default "";, allowing users to provide string-based metadata with a fallback.[13]
The applicability of an annotation is governed by targets, which specify the program elements where the annotation can be applied. These targets are defined using the @Target meta-annotation, whose value is an array of ElementType enum constants. Common targets include TYPE for classes, interfaces, or enums; FIELD for fields (including enum constants); METHOD for methods; PARAMETER for formal parameters; CONSTRUCTOR for constructors; LOCAL_VARIABLE for local variables; ANNOTATION_TYPE for other annotation types; and PACKAGE for packages.[14] Additional targets introduced in Java SE 8, such as TYPE_PARAMETER and TYPE_USE, extend applicability to type parameters and type uses like casts or implements clauses.[14] If no @Target is specified, the annotation defaults to all element types.[6]
Retention policies determine the lifecycle of annotations, controlling their visibility from source code through runtime. Specified via the @Retention meta-annotation with a RetentionPolicy enum value, the policies are: SOURCE, where annotations are used only during compilation and discarded afterward; CLASS (the default), where they are stored in the class file but not accessible at runtime; and RUNTIME, where they persist in the class file and remain available for reflection at runtime, enabling dynamic processing by tools or frameworks.[15] This mechanism balances compile-time efficiency with runtime flexibility, as runtime retention incurs a minor performance overhead due to class file size and reflection access.[5]
Repeatable annotations, introduced in Java SE 8, allow multiple instances of the same annotation type to be applied to a single program element, enhancing expressiveness for scenarios requiring lists of metadata. To enable this, the annotation type is marked with the @Repeatable meta-annotation, referencing a container annotation type that holds an array of the repeatable type as its value element.[8] The compiler automatically synthesizes the container when multiple instances are used, and retrieval at runtime is supported via reflection methods like getAnnotationsByType(Class<T>).[16] This feature requires the container to be properly defined with appropriate targets and retention matching the repeatable annotation.[8]
History and Evolution
Introduction in Java 5
Java annotations were introduced as a core language feature in Java SE 5.0, released in September 2004, through JSR 175: A Metadata Facility for the Java Programming Language. This specification, developed under the Java Community Process, aimed to standardize the addition of metadata to program elements such as classes, interfaces, fields, and methods. The expert group included representatives from Sun Microsystems, Oracle, Borland, and others, with key contributions from language designers like Neal Gafter, who played a central role in integrating these changes into the Java compiler and language specification.[11][17] The motivation for annotations stemmed from the need to replace fragmented, ad hoc metadata mechanisms prevalent in earlier Java development, such as marker interfaces for simple type indications and verbose XML files for configuring tools like Javadoc, compilers, and frameworks including Enterprise JavaBeans (EJB). These approaches often led to boilerplate code, maintenance challenges, and separation of metadata from source code, hindering developer productivity and tool integration. JSR 175 sought to provide a lightweight, declarative syntax embedded directly in Java source, enabling better support for compile-time and runtime processing without extending the core type system.[11] Among the key features introduced were the foundational syntax for declaring and applying annotations, the ability to define custom annotations with type-safe elements, and three built-in annotations in thejava.lang package: @Override, which verifies that a method overrides a superclass method; @Deprecated, which signals that an element is outdated and should not be used in new code; and @SuppressWarnings, which allows selective suppression of compiler warnings for specific code elements. Annotations were also tightly integrated with the Java reflection API, providing methods in classes like java.lang.reflect.Method and java.lang.Class to access annotation metadata at runtime, thus enabling dynamic processing by libraries and tools.[4][11]
Initial adoption of annotations occurred rapidly in the Java ecosystem, particularly in object-relational mapping (ORM) frameworks. For instance, Hibernate 3.0, released in February 2005, incorporated annotations to support EJB 3.0-style entity mappings, allowing developers to replace XML descriptors with inline metadata for persistence configurations, which streamlined development and reduced configuration overhead in enterprise applications. This early integration highlighted annotations' potential for framework extensibility and paved the way for broader annotation-driven programming patterns.[18]
Developments in Later Versions
Java 6 integrated annotation processing directly into the javac compiler, eliminating the need for the separate apt tool introduced in Java 5 and enabling automatic discovery of pluggable processors via the ServiceLoader mechanism.[19] This improvement allowed developers to configure annotation processors more seamlessly during compilation, supporting the standardized API from JSR 269 while enhancing tool integration and runtime efficiency.[20] Java 8 introduced repeatable annotations through JEP 120, permitting multiple instances of the same annotation type on a single program element to improve code readability without requiring wrapper container annotations.[21] Additionally, JSR 308 extended annotation targets to include type uses, such as generic arguments, type casts, and method receivers, enabling more precise type checking and pluggable type systems.[22] These features updated reflective APIs and class file formats to handle repeated and type annotations, broadening their applicability in static analysis tools. The apt tool and its associated com.sun.mirror API, deprecated since Java 7, were fully removed in this release.[21] In Java 9, the Java Platform Module System (JPMS) introduced support for annotations on modules via the new ElementType.MODULE target, allowing annotations directly on module-info.java declarations to describe module properties or enforce policies. The standardized javax.annotation.processing API was recommended for all processing tasks to align with modularization goals.[23] Subsequent releases up to Java 25 enhanced annotation support in modern language features without altering core syntax. Records, finalized in Java 16, permit annotations on record components, which propagate to generated fields, constructors, and methods, facilitating validation and serialization in data classes.[24] Sealed classes and interfaces, standardized in Java 17, integrate annotations seamlessly on sealed types and their permitted subclasses, supporting exhaustive pattern matching in switch expressions where annotated hierarchies can be deconstructed more safely.[25] Pattern matching enhancements in Java 21, including guarded patterns and null handling, leverage these structures but do not introduce new annotation mechanisms, instead relying on existing processing for compile-time validation.[26] No major changes to annotations were introduced in Java 22 through 25.[27] These developments have significantly impacted library ecosystems, enabling tools like Project Lombok to leverage annotations for automated code generation, such as producing getters, setters, equals, and constructors from simple declarations like @Data, thereby reducing boilerplate while maintaining compatibility with evolving Java features.[28]Syntax and Declaration
Annotation Declaration
Custom annotation types in Java are declared using the@interface keyword, which defines a new annotation type that implicitly extends the java.lang.annotation.[Annotation](/page/Annotation) interface.[3] The declaration resembles an interface but contains only method declarations that represent the annotation's elements, with no constructors, instance fields, or implementation code allowed.[29] For example, a basic annotation type might be declared as follows:
@interface MyAnnotation {
String value();
}
@interface MyAnnotation {
String value();
}
int, boolean), String, Class, enumerations, other annotations, and arrays of these types; no other return types are permitted to maintain compile-time constancy.[3] An extends clause is prohibited in the declaration, as the type automatically inherits from Annotation, and methods must have no parameters or throws clauses.[29] Default values can be assigned to elements using the = operator, allowing optional parameters when the annotation is applied; these defaults must be compile-time constants of the supported types.[3] For instance:
@interface MyAnnotation {
[String](/page/String) value() default "";
int priority() default 1;
}
@interface MyAnnotation {
[String](/page/String) value() default "";
int priority() default 1;
}
value to allow abbreviated application syntax, such as @MyAnnotation("example") instead of @MyAnnotation(value = "example").[29] Meta-annotations like @Target and @Retention can be applied to the declaration to specify where and how the annotation may be used, but their details are covered elsewhere.[3]
Applying Annotations
Annotations in Java are applied by prefixing the annotation type name with an at-sign (@) immediately before the declaration of the program element it modifies, such as a class, method, field, or parameter. This placement treats the annotation as a modifier, similar to keywords likepublic or static, and it must appear before other modifiers. For instance, the syntax @MyAnnotation public class Example { } attaches the @MyAnnotation to the class declaration.[1]
When specifying element values for an annotation, parentheses enclose the pairs in the form elementName = value, with values being compile-time constants like strings, primitives, enums, classes, or annotation instances. For marker annotations without elements, the parentheses are optional and often omitted, as in @Preliminary. Single-element annotations, where the element is named value, allow omission of the name and equals sign, simplifying to @Annotation("value"). Multiple elements are listed comma-separated within the parentheses, such as @RequestForEnhancement(id=2868724, synopsis="Enable time-travel", engineer="Duke", date=1492).[3]
Multiple distinct annotations can be stacked sequentially before a single element declaration, applying each to the same target. For repeatable annotations, introduced in Java 8, the same annotation type can be applied multiple times to the same element if marked with the @Repeatable meta-annotation, which specifies a container annotation to hold the array of instances. Without @Repeatable, repetition requires explicitly using the container, like @Schedules({@Schedule(...), @Schedule(...)}); with it, the compiler handles the container implicitly, allowing direct repetition such as @Schedule(...) @Schedule(...).[8]
Type annotations, available since Java 8, extend this syntax to annotate not just declarations but any use of a type, including generics, casts, and method parameters, to support pluggable type checking. The annotation precedes the type identifier, as in @NonNull String str for a variable or @NonNull [List](/page/List)<String> for a parameterized type, ensuring annotations apply to the type itself rather than the declaration. These enhance analysis for issues like nullability without altering program semantics.[7]
Application of annotations is restricted by the @Target meta-annotation on their declaration, which specifies allowable program elements (e.g., ElementType.METHOD limits use to methods). Annotations cannot be attached to arbitrary expressions or statements outside these targets, ensuring they modify only valid declarations or type uses. Violations result in compile-time errors.[3]
Built-in Annotations
Core Standard Annotations
The core standard annotations in Java, defined in thejava.lang package, provide essential mechanisms for enhancing code correctness, maintenance, and compiler feedback. These annotations—@Override, @Deprecated, @SuppressWarnings, and @SafeVarargs—are primarily applied at the source level to signal intentions to the compiler, helping developers avoid common errors and manage code evolution without altering runtime behavior.[30] They are integral to everyday Java programming, supporting features like method overriding validation and warning suppression, and have remained stable since their introduction in Java 5 and 7.[31]
@Override indicates that a method declaration is intended to override a method from a supertype, such as a superclass or implemented interface.[31] The compiler enforces this by generating an error if the annotated method does not actually override a supertype method or if its signature is not override-equivalent to a public method in java.lang.Object (e.g., equals, hashCode, or toString).[32] This annotation targets only methods (@Target(METHOD)) and has source retention (@Retention(SOURCE)), meaning it is not retained in the compiled class file.[31] It prevents subtle bugs from accidental overloading instead of overriding, such as typos in method names or mismatched parameter types, thereby promoting reliable inheritance hierarchies.[33] For example:
class Parent {
public void doSomething() { }
}
class Child extends Parent {
@Override
public void doSomething() { } // Valid override
}
class Parent {
public void doSomething() { }
}
class Child extends Parent {
@Override
public void doSomething() { } // Valid override
}
doSomthing, the compiler would error, catching the mistake early.[32] Introduced in Java 5, @Override applies to record component accessor methods as well in modern Java.[31]
@Deprecated marks a program element—such as a class, method, field, or parameter—as outdated, signaling to developers that it should not be used due to potential issues, replacement by better alternatives, or planned removal in future versions.[34] When a deprecated element is used or overridden in non-deprecated code, the compiler issues a warning, but the element remains functional and does not trigger warnings for local variables, parameters, or packages.[35] It supports two attributes since Java 9: since (a string indicating the version of deprecation, default empty) and forRemoval (a boolean flagging imminent removal, default false), which refine the deprecation intent.[34] For documentation, the corresponding @deprecated Javadoc tag must be used to explain the reason and suggest alternatives, ensuring alignment with the annotation.[35] In modules, deprecation warns on requires directives but not on exports or opens.[34] An example usage:
@Deprecated(since="1.5", forRemoval=true)
public void oldMethod() { }
@Deprecated(since="1.5", forRemoval=true)
public void oldMethod() { }
@Target includes TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, PACKAGE, MODULE).[34]
@SuppressWarnings allows developers to suppress specific compiler warnings within an annotated element and its sub-elements, reducing noise from known issues without disabling all warnings globally.[36] It requires a value attribute as a String[] specifying warning categories, such as "unchecked" for generic type safety issues, "deprecation" for deprecated element usage, "removal" for elements marked for removal, or "preview" for preview features; unrecognized or duplicate names are ignored without error.[37] The annotation applies to types, fields, methods, parameters, constructors, and local variables (@Target includes those), with source retention, and is most effective when placed on the innermost relevant element (e.g., a method rather than the enclosing class) to avoid over-suppression.[36] Compilers must support JLS-mandated warnings but may add vendor-specific ones.[37] For instance:
@SuppressWarnings("unchecked")
public void processList(List list) {
// Suppresses unchecked warning for raw type usage
}
@SuppressWarnings("unchecked")
public void processList(List list) {
// Suppresses unchecked warning for raw type usage
}
@Target({CONSTRUCTOR, METHOD})) with runtime retention (@Retention(RUNTIME)), allowing reflection-based checks.[38] Heap pollution occurs when mismatched types lead to runtime ClassCastException, which @SafeVarargs helps avoid by signaling safe usage.[40] Example:
@SafeVarargs
public static <T> List<T> combine(List<T>... lists) {
// Safe varargs handling assumed
return Arrays.asList(lists);
}
@SafeVarargs
public static <T> List<T> combine(List<T>... lists) {
// Safe varargs handling assumed
return Arrays.asList(lists);
}
Specialized Built-in Annotations
In addition to the core annotations, several specialized built-in annotations—spanning thejava.lang package and other standard packages—support advanced language features, API integrations, and tool usage. These annotations enable validation for functional programming constructs, marking generated code, and facilitating testing frameworks, among other specialized roles. They are integral to Java's evolution, particularly from Java 8 onward, enhancing modularity and developer productivity without altering core language semantics.[4]
The @FunctionalInterface annotation, defined in the java.lang package since Java 8, serves as an informative marker for interfaces intended to represent lambda expressions or method references. It indicates that the annotated interface should have exactly one abstract method, allowing the compiler to enforce this rule and prevent errors in functional interface usage. For example, applying @FunctionalInterface to an interface with multiple abstract methods results in a compilation error, ensuring compatibility with lambda targets. This annotation supports the introduction of functional programming in Java, where such interfaces are ubiquitous in the java.util.function package and Stream API.[41]
The @Generated annotation, part of javax.annotation.processing since Java 8, marks source code produced by automated tools, such as code generators or processors. It includes attributes for the generating tool's name, version, date, and comments, aiding static analysis tools in distinguishing generated code from manually written code—often to suppress warnings or linting on auto-generated elements. For instance, IDEs and build tools like Maven or Gradle use this to avoid unnecessary checks on boilerplate code from frameworks like JPA or Lombok. This annotation promotes better integration between annotation processors and development workflows.[42]
These specialized annotations also contextualize broader Java features: @FunctionalInterface bolsters lambda support in Java 8, @Generated aids annotation processors in modular builds from Java 9 onward, collectively improving code maintainability and feature-specific validations.
Meta-Annotations
Retention and Target Policies
Java annotations utilize meta-annotations to define their behavior, with @Retention and @Target playing key roles in controlling lifecycle and applicability. The @Retention meta-annotation specifies the retention policy for an annotation type, determining how long it persists through the compilation and execution process.[43] This policy is essential for deciding whether annotations are available for processing at compile time, in bytecode, or at runtime via reflection.[4] The RetentionPolicy enum provides three values to guide this behavior. RetentionPolicy.SOURCE indicates that the annotation is discarded by the compiler and retained only in source code, making it suitable for tools like linters or annotation processors that operate solely at compile time without affecting the final bytecode.[44] RetentionPolicy.CLASS, the default if @Retention is omitted, records the annotation in the class file during compilation but discards it at runtime, allowing access by bytecode manipulation tools while minimizing runtime overhead.[44] RetentionPolicy.RUNTIME ensures the annotation is recorded in the class file and retained by the virtual machine, enabling reflective access during program execution for dynamic behaviors like framework integrations.[44] In declaration, @Retention is applied directly to the annotation interface, as shown in this example:import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Target(ElementType.FIELD)
public @interface FieldAnnotation {}
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Target(ElementType.FIELD)
public @interface FieldAnnotation {}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MyAnno {}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MyAnno {}
Documentation and Inheritance Meta-Annotations
The@Documented meta-annotation, defined in the java.lang.annotation package, indicates that annotations of a particular type are to be considered part of the public contract of the elements they annotate, ensuring their inclusion in generated documentation such as Javadoc.[49] When applied to an annotation interface declaration, it instructs documentation tools to include instances of that annotation in the output for documented elements like classes, methods, or fields; without @Documented, such annotations are typically excluded from API documentation to avoid cluttering public interfaces.[49] For instance, custom annotations marked with @Documented will appear in Javadoc alongside standard documented elements, promoting transparency in API usage.[3] This meta-annotation targets only annotation types (@Target(ElementType.ANNOTATION_TYPE)) and has runtime retention by default, though its primary impact is on build-time documentation generation.[49]
In contrast, the @Inherited meta-annotation enables automatic propagation of class-level annotations to subclasses, facilitating inheritance of metadata in object-oriented hierarchies.[50] When @Inherited is applied to an annotation type declaration targeted at types (classes), any instance of that annotation on a superclass is implicitly inherited by its subclasses unless explicitly overridden by a subclass-specific annotation.[50] This behavior is queried via reflection methods like getAnnotation(), which traverse the class hierarchy upward until the annotation is found or the Object class is reached; if absent throughout, the subclass is treated as unannotated.[50] For example, applying @Inherited to a custom annotation on a base class ensures subclasses automatically acquire it for type-related metadata, such as configuration or behavioral markers.[3]
Both meta-annotations have specific limitations to their applicability. @Documented affects only documentation tools and does not influence runtime behavior or annotation retention policies, which are handled separately.[49] Similarly, @Inherited is restricted to annotations with a TYPE target on classes and has no effect on annotations applied to methods, fields, constructors, or other elements, nor does it propagate annotations from implemented interfaces to implementing classes.[50] These constraints ensure predictable behavior in inheritance and documentation contexts without unintended side effects across diverse annotation uses.[3]
Processing Mechanisms
Compile-Time Processing
Compile-time processing of Java annotations occurs during the compilation phase, leveraging the Pluggable Annotation Processing API defined in JSR 269 to enable developers to create custom processors that analyze annotated source code and generate additional artifacts or perform validations.[20] This mechanism integrates directly with the javac compiler, allowing processors to hook into the build process without runtime overhead.[51] The core of this API is thejavax.annotation.processing package, which provides interfaces and classes for implementing annotation processors.[51] Processors must implement the Processor interface, which includes methods like process to handle sets of annotated elements and getSupportedAnnotationTypes to declare the annotations they target.[52] Discovery of processors happens automatically via the service provider mechanism: implementations are registered in META-INF/services/javax.annotation.processing.Processor files within their JARs, and the compiler loads them at build time.[51]
Processing follows a round-based lifecycle managed by the annotation processing tool environment, typically the compiler itself.[51] In each round, the tool scans source files for annotations supported by registered processors and passes subsets of annotated program elements—such as types, methods, or fields—to the relevant process methods.[52] Processors can then query element details using the Elements utility, generate new source files or resources via the Filer interface, or issue diagnostic messages (warnings or errors) through the Messager.[51] Rounds continue iteratively until no new annotations are found or no further changes are made to the source, ensuring completeness while avoiding infinite loops; the environment signals the end when processing is claimed complete by all processors.[51]
This framework supports code generation and boilerplate reduction in popular tools. For instance, Project Lombok uses annotation processors to automatically generate methods like getters, setters, and constructors based on annotations such as @Data, reducing repetitive code in domain classes. Similarly, Google's Auto library employs processors in components like AutoValue and AutoService to generate value classes and service provider registrations from annotations, streamlining immutable data and SPI implementations. In a typical flow, a processor registers support for a custom annotation like @MyAnnotation, receives annotated elements during a round, analyzes their structure (e.g., via Element.getAnnotation), and outputs new .java files to the Filer's source output stream, which are then compiled in subsequent rounds.[51] This build-time approach contrasts with runtime processing by producing static code enhancements rather than dynamic introspection.[51]
Runtime Processing
Runtime processing of Java annotations occurs through the reflection API, enabling dynamic inspection and utilization of annotations during program execution. Thejava.lang.reflect package provides the AnnotatedElement interface, implemented by classes such as Class, Method, Field, and Constructor, which represent program elements that can bear annotations. Key methods include getAnnotation(Class<T> annotationClass), which returns the annotation of the specified type if present on the element, or null otherwise, and getAnnotations() to retrieve all annotations on the element. These methods facilitate runtime access to annotation metadata, such as attribute values, allowing frameworks to configure behavior based on annotated elements.[53]
Access to annotations at runtime is governed by the @Retention meta-annotation; only those declared with RetentionPolicy.RUNTIME are preserved in the class file and visible via reflection, while SOURCE or CLASS retention policies make annotations unavailable during execution. This design ensures that runtime-visible annotations incur minimal overhead for non-runtime cases, as the compiler discards them from the bytecode. For instance, the Java Virtual Machine retains runtime annotations in dedicated class file attributes, enabling reflective queries without additional storage for discarded ones.[3][54]
In practice, runtime annotation processing powers key framework features. The Spring Framework employs reflection to detect @Autowired annotations on fields, constructors, or methods, automatically injecting dependencies during bean initialization via post-processors like AutowiredAnnotationBeanPostProcessor. Similarly, Jakarta Bean Validation uses @Valid to trigger runtime constraint validation on annotated objects, inspecting bean properties reflectively to enforce rules defined by annotations like @NotNull or @Size. These mechanisms rely on runtime retention to enable such dynamic behaviors without compile-time code generation.[55][56]
Performance considerations arise due to reflection's inherent overhead, as accessing annotations involves proxy-based invocation and security checks that bypass direct bytecode access, potentially hindering Just-In-Time (JIT) compiler optimizations. Reflective calls incur a performance overhead compared to direct method invocations, typically around 23% in benchmarks but potentially much higher in hot paths due to lack of JIT inlining and additional runtime checks, though this impact is negligible in infrequent or I/O-bound scenarios. To mitigate, applications should cache annotation results—such as storing retrieved Annotation instances in maps keyed by AnnotatedElement—for repeated queries, reducing redundant lookups in frameworks like Spring.[57]
Practical Examples
Basic Usage Examples
Java annotations provide a way to add metadata to Java program elements such as classes, methods, and fields. A basic custom annotation is declared using the@interface keyword, defining elements as abstract methods that represent the annotation's attributes. For example, the following declares a simple annotation type named SimpleAnno with a single string element msg() and specifies runtime retention using the @Retention meta-annotation, allowing the annotation to be accessible via reflection during program execution.[29][4]
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface SimpleAnno {
[String](/page/String) msg();
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface SimpleAnno {
[String](/page/String) msg();
}
test() method is annotated with SimpleAnno, assigning the value "Hello" to the msg element.[1]
public class ExampleClass {
@SimpleAnno(msg = "Hello")
[public](/page/Public) void [test](/page/.test)() {
[System](/page/System).out.println("Annotated method");
}
}
public class ExampleClass {
@SimpleAnno(msg = "Hello")
[public](/page/Public) void [test](/page/.test)() {
[System](/page/System).out.println("Annotated method");
}
}
getAnnotation() method on a Method object returns the annotation if present, allowing access to its elements. The following code demonstrates this by getting the SimpleAnno from the test() method, extracting the msg value, and printing it. This requires the annotation to have RetentionPolicy.RUNTIME for visibility at runtime.[58]
import java.lang.reflect.Method;
public class AnnotationReader {
public static void main(String[] args) throws Exception {
Method method = ExampleClass.class.getMethod("test");
SimpleAnno anno = method.getAnnotation(SimpleAnno.class);
if (anno != null) {
System.out.println("Message: " + anno.msg()); // Outputs: Message: Hello
}
}
}
import java.lang.reflect.Method;
public class AnnotationReader {
public static void main(String[] args) throws Exception {
Method method = ExampleClass.class.getMethod("test");
SimpleAnno anno = method.getAnnotation(SimpleAnno.class);
if (anno != null) {
System.out.println("Message: " + anno.msg()); // Outputs: Message: Hello
}
}
}
@Override offer simple usage for common scenarios without custom declarations. This annotation is applied to a method in a subclass to indicate it overrides a superclass method, helping the compiler verify the override and catch errors. For instance, in a subclass Child extending Parent, the myMethod() is marked with @Override to explicitly signal the overriding intent.[1][4]
class Parent {
public void myMethod() {
System.out.println("Parent method");
}
}
class Child extends Parent {
@Override
public void myMethod() {
System.out.println("Child overriding method");
}
}
class Parent {
public void myMethod() {
System.out.println("Parent method");
}
}
class Child extends Parent {
@Override
public void myMethod() {
System.out.println("Child overriding method");
}
}
Advanced Annotation Processing Example
To demonstrate advanced annotation processing for code generation, consider a custom marker annotation@Getter targeted at methods, used to indicate setter methods for which corresponding getter methods should be automatically generated. This annotation has no elements and is defined as follows:
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
public @interface Getter {}
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
public @interface Getter {}
@Target(ElementType.METHOD) meta-annotation restricts the annotation to method declarations, ensuring it applies only to methods such as setters. In practice, libraries like Project Lombok use similar annotations on fields to generate getters and setters, often via bytecode manipulation rather than source generation.[59]
An annotation processor for @Getter must implement the Processor interface, conventionally by extending AbstractProcessor from the javax.annotation.processing package. The processor declares support for the @Getter annotation via the @SupportedAnnotationTypes meta-annotation and overrides the process method to scan source elements. In this method, the processor uses the RoundEnvironment to retrieve elements annotated with @Getter, filters for method elements, and infers getter method signatures from the setter method names (e.g., transforming setProperty to getProperty). It then employs the Filer from the ProcessingEnvironment to generate a new source file for a companion class containing the getter methods. A simplified implementation might look like this (assuming the annotation is in package com.example):
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.lang.model.util.ElementFilter;
import javax.tools.JavaFileObject;
import java.io.PrintWriter;
import java.util.Set;
@SupportedAnnotationTypes("com.example.Getter")
@SupportedSourceVersion(SourceVersion.RELEASE_17)
public class GetterProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (roundEnv.processingOver()) {
return false;
}
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Getter.class);
for (Element element : ElementFilter.methodsIn(elements)) {
ExecutableElement method = (ExecutableElement) element;
if (!isValidSetter(method)) continue;
TypeElement classElement = (TypeElement) element.getEnclosingElement();
String setterName = method.getSimpleName().toString();
String getterName = inferGetterName(setterName);
VariableElement param = method.getParameters().get(0);
String fieldName = param.getSimpleName().toString(); // Assume field name matches param
String returnType = param.asType().toString();
generateGetter(classElement, getterName, returnType, fieldName);
}
return true; // Claim the annotations
}
private boolean isValidSetter(ExecutableElement method) {
return method.getParameters().size() == 1 && method.getSimpleName().toString().startsWith("set");
}
private String inferGetterName(String setterName) {
return "get" + setterName.substring(3);
}
private void generateGetter(TypeElement classElement, String getterName, String returnType, String fieldName) {
try {
String className = classElement.getSimpleName() + "Getters";
String packageName = getPackageName(classElement);
JavaFileObject file = processingEnv.getFiler().createSourceFile(packageName + "." + className);
try (PrintWriter writer = new PrintWriter(file.openWriter())) {
writer.println("package " + packageName + ";");
writer.println();
writer.println("import " + classElement.getQualifiedName() + ";");
writer.println();
writer.println("public class " + className + " {");
writer.println(" public static " + returnType + " " + getterName + "(" + classElement.getSimpleName() + " instance) {");
writer.println(" return instance." + fieldName + ";");
writer.println(" }");
writer.println("}");
}
} catch (Exception e) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage(), null);
}
}
private String getPackageName(TypeElement classElement) {
Element enclosing = classElement.getEnclosingElement();
if (enclosing instanceof PackageElement) {
return ((PackageElement) enclosing).getQualifiedName().toString();
}
return "";
}
}
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.lang.model.util.ElementFilter;
import javax.tools.JavaFileObject;
import java.io.PrintWriter;
import java.util.Set;
@SupportedAnnotationTypes("com.example.Getter")
@SupportedSourceVersion(SourceVersion.RELEASE_17)
public class GetterProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (roundEnv.processingOver()) {
return false;
}
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Getter.class);
for (Element element : ElementFilter.methodsIn(elements)) {
ExecutableElement method = (ExecutableElement) element;
if (!isValidSetter(method)) continue;
TypeElement classElement = (TypeElement) element.getEnclosingElement();
String setterName = method.getSimpleName().toString();
String getterName = inferGetterName(setterName);
VariableElement param = method.getParameters().get(0);
String fieldName = param.getSimpleName().toString(); // Assume field name matches param
String returnType = param.asType().toString();
generateGetter(classElement, getterName, returnType, fieldName);
}
return true; // Claim the annotations
}
private boolean isValidSetter(ExecutableElement method) {
return method.getParameters().size() == 1 && method.getSimpleName().toString().startsWith("set");
}
private String inferGetterName(String setterName) {
return "get" + setterName.substring(3);
}
private void generateGetter(TypeElement classElement, String getterName, String returnType, String fieldName) {
try {
String className = classElement.getSimpleName() + "Getters";
String packageName = getPackageName(classElement);
JavaFileObject file = processingEnv.getFiler().createSourceFile(packageName + "." + className);
try (PrintWriter writer = new PrintWriter(file.openWriter())) {
writer.println("package " + packageName + ";");
writer.println();
writer.println("import " + classElement.getQualifiedName() + ";");
writer.println();
writer.println("public class " + className + " {");
writer.println(" public static " + returnType + " " + getterName + "(" + classElement.getSimpleName() + " instance) {");
writer.println(" return instance." + fieldName + ";");
writer.println(" }");
writer.println("}");
}
} catch (Exception e) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage(), null);
}
}
private String getPackageName(TypeElement classElement) {
Element enclosing = classElement.getEnclosingElement();
if (enclosing instanceof PackageElement) {
return ((PackageElement) enclosing).getQualifiedName().toString();
}
return "";
}
}
ElementFilter to identify annotated methods, validates they are setters, derives the getter logic from method metadata including inferring the field from the parameter name, and invokes Filer.createSourceFile to produce a new .java file with the generated getter as a static method in a companion class, avoiding modification of the original source. The processingEnv.getFiler() provides the Filer instance during init. Returning true from process indicates the processor has handled the annotations, preventing further processing in subsequent rounds unless new sources are generated.[60][61][19]
For automatic discovery by the compiler, the processor is registered via the Java service loader mechanism. Create a file at META-INF/services/javax.annotation.processing.Processor in the processor's JAR, containing the fully qualified class name (e.g., com.example.GetterProcessor). This file lists available processors, allowing javac to load them without explicit specification.[19]
To compile and invoke the processor, place the processor JAR on the classpath or processor path and use the javac command with the -processor option. For instance, assuming a source file MyClass.java with a setter method annotated @Getter:
javac -processor com.example.GetterProcessor MyClass.java
javac -processor com.example.GetterProcessor MyClass.java
-s), which is then compiled into the output. The resulting bytecode includes the automatically generated getters, enhancing code generation without manual implementation. If only processing is desired without full compilation, use -proc:only.[19]