Criação e Uso de Anotações Personalizadas em Java
Além das anotações fornecidas pela linguagem (como @Override, @Deprecated e @FunctionalInterface), o Java permite criar anotações personalizadas — úteis para marcar e configurar elementos do código-fonte.
Essas anotações são definidas com o modificador @interface, que cria uma estrutura semelhante a um interface, mas destinada a armazenar metadados:
// Declaração de uma anotação chamada MyAnnotation
@interface MyAnnotation { }Assim como interfaces comuns, as anotações são convertidas em bytecode e ficam acessíveis para ferramentas, compiladores e frameworks.
Por padrão, todas as anotações estendem implicitamente a interface java.lang.annotation.Annotation.
Aplicando uma anotação
@interface MyAnnotation { }
@MyAnnotation
class Thing {
@MyAnnotation
String name;
}Aqui, a anotação @MyAnnotation foi aplicada tanto à classe quanto ao campo name.
Por padrão, sem restrições, uma anotação pode ser usada em qualquer declaração, exceto em parâmetros de tipo ou uso de tipo (genéricos, casts etc.).
Meta-anotações
O Java fornece anotações especiais que servem para configurar o comportamento de outras anotações. Essas são chamadas de meta-anotações, e as mais usadas são:
| Meta-anotação | Descrição |
|---|---|
@Target | Define onde a anotação pode ser aplicada (classe, método, campo, etc.) |
@Retention | Define até quando a anotação será mantida (em tempo de compilação, classe ou execução) |
@Documented | Indica que a anotação deve aparecer na documentação Javadoc |
@Inherited | Faz com que subclasses herdem anotações aplicadas à superclasse |
@Repeatable | Permite aplicar a mesma anotação mais de uma vez ao mesmo elemento |
@Target
A meta-anotação @Target define em quais elementos a anotação pode ser usada.
Ela recebe um ou mais valores de ElementType, que indicam o tipo de elemento suportado.
Exemplo:
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target({ElementType.FIELD, ElementType.RECORD_COMPONENT, ElementType.TYPE})
@interface MyAnnotation { }Essa definição permite aplicar @MyAnnotation a campos, classes/interfaces e componentes de records:
@MyAnnotation
class Thing {
@MyAnnotation
String name;
}Aplicações em outros contextos (como métodos) gerariam erro de compilação.
Elementos de anotações
Os elementos de uma anotação são definidos dentro de seu corpo e se comportam como métodos sem parâmetros:
@interface MyAnnotation {
String value();
int count();
}Ao aplicar a anotação, você deve fornecer valores para esses elementos:
@MyAnnotation(value = "Thing", count = 1)
class Thing { }A sintaxe se assemelha a uma chamada de construtor com argumentos nomeados.
Valores padrão para elementos
Os elementos podem ter valores padrão, definidos com a palavra-chave default:
@interface MyAnnotation {
String value() default "DefaultValue";
int count() default 0;
}Assim, os valores podem ser omitidos ao aplicar a anotação:
@MyAnnotation(value = "TypeA", count = 2)
class TypeA { }
@MyAnnotation(value = "TypeB") // count usa valor padrão (0)
class TypeB { }
@MyAnnotation(count = 1) // value usa valor padrão
class TypeC { }
@MyAnnotation // ambos usam valores padrão
class TypeD { }Elementos do tipo array
Elementos também podem ser arrays, com valores padrão entre chaves {}:
@interface MyAnnotation {
String[] emails() default {"user1@mail.com", "user2@mail.com"};
String[] tags() default {}; // array vazio
}Esses valores padrão são avaliados dinamicamente — não são armazenados literalmente na anotação.
@Retention
A meta-anotação @Retention controla quanto tempo a anotação é mantida e onde ela pode ser acessada.
Ela recebe um valor do enum RetentionPolicy:
| Política | Descrição |
|---|---|
SOURCE | A anotação existe apenas no código-fonte e é descartada na compilação. Ideal para ferramentas de análise de código. |
CLASS | A anotação é gravada no bytecode (.class), mas não é acessível em tempo de execução. (Valor padrão) |
RUNTIME | A anotação é preservada e pode ser lida via reflexão (Reflection) durante a execução. |
Exemplo:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.RUNTIME) // Disponível em tempo de execução
@Target(ElementType.TYPE) // Aplicável a classes e interfaces
@interface MyAnnotation {
String value() default "DefaultValue";
}Com RetentionPolicy.RUNTIME, frameworks e ferramentas podem ler e agir sobre anotações usando a API de reflexão, o que é essencial para tecnologias como Spring, JUnit, Jakarta EE, entre outras.
Resumo
| Conceito | Função |
|---|---|
@interface | Define uma nova anotação personalizada |
@Target | Especifica onde a anotação pode ser usada |
@Retention | Define até quando a anotação é mantida (código-fonte, bytecode ou execução) |
| Valores padrão | Permitem omitir parâmetros ao aplicar a anotação |
| Meta-anotações | Anotações que configuram o comportamento de outras anotações |
Conclusão
Criar anotações personalizadas em Java é o primeiro passo para explorar o poder dos metadados e da configuração declarativa.
Combinadas a meta-anotações como @Target e @Retention, elas se tornam ferramentas fundamentais para o desenvolvimento de frameworks, validações automáticas e injeção de dependências.