Anotações Integradas do Java
Anotações Java integradas mais usadas — @Override, @Deprecated, @SuppressWarnings, @SafeVarargs, @FunctionalInterface.
A biblioteca padrão inclui um pequeno conjunto de anotações em java.lang que o compilador trata de forma especial. Nenhuma delas adiciona comportamento ao seu código em tempo de execução; todas são dicas para o javac (ou, no caso de @Deprecated, para o conjunto de ferramentas em geral). Saber o que cada uma garante — e o que ela não garante — é a parte prática de trabalhar com anotações no dia a dia.
As cinco que você verá com mais frequência:
@Override— "este método substitui um de um supertipo."@Deprecated— "não use isto; ele será removido."@SuppressWarnings— "silencie estas advertências específicas neste escopo."@SafeVarargs— "meu método varargs não polui o heap."@FunctionalInterface— "esta interface tem exatamente um método abstrato."
@Override
Colocar @Override em um método informa ao compilador que o método pretende substituir um método da superclasse ou implementar um método de interface. Se ele não substituir realmente nada (porque o nome está com erro de digitação, a assinatura está errada ou o método pai foi removido), o javac falha na compilação.
class Animal {
public String speak() { return "?"; }
}
class Dog extends Animal {
@Override
public String speak() { return "woof"; } // OK
@Override
public String spaek() { return "oops"; } // compile error: nothing to override
}Ele detecta o tipo de bug muito difícil de encontrar em tempo de execução: um método de substituição cuja assinatura diverge da superclasse. Sempre escreva @Override em métodos que você pretende substituir. Ele tem retenção SOURCE, portanto desaparece no momento em que a compilação termina.
@Deprecated
@Deprecated marca algo — classe, método, campo, construtor — como desencorajado. O compilador avisa em cada local de uso. Desde o Java 9, a anotação aceita dois elementos:
@Deprecated(since = "1.4", forRemoval = true)
public void oldApi() { ... }sincedocumenta quando a depreciação começou.forRemoval = trueé um sinal mais forte: uma versão futura planeja remover a API. O compilador emite um aviso de remoção (geralmente mais intenso do que um aviso de depreciação simples) e a ferramenta Javadoc o sinaliza de forma diferente.
Ao contrário de @Override, @Deprecated tem retenção RUNTIME — ferramentas de bytecode, IDEs e reflexão podem vê-la. A tag Javadoc complementar @deprecated (minúscula, em um comentário de documentação) carrega a explicação; a anotação aciona as ferramentas.
@SuppressWarnings
Quando o compilador está certo quase sempre, mas errado aqui, @SuppressWarnings silencia uma categoria de advertências dentro do elemento anotado:
@SuppressWarnings("unchecked")
List<String> strings = (List<String>) raw; // raw cast intentional
@SuppressWarnings({"unchecked", "rawtypes"})
public void uglyButNeeded() { ... }O elemento string nomeia uma categoria de advertência; as mais comuns são unchecked, rawtypes, deprecation, serial, unused, removal. Os compiladores podem aceitar outras. Duas regras para manter isso organizado:
- Anote o menor escopo possível. Suprima em uma variável local ou em um único método, nunca em uma classe, a menos que cada linha realmente precise.
- Combine com um comentário explicando o motivo. Um
@SuppressWarnings("unchecked")simples ao lado de um cast deixa o próximo leitor sem saber se o cast é realmente seguro.
@SafeVarargs
Um método varargs cujo tipo de parâmetro contém um parâmetro de tipo tem um problema sutil: no local de chamada, o compilador pode precisar criar um array de um tipo genérico, o que é inseguro. O compilador avisa sobre isso com a mensagem "possível poluição do heap". Se o autor tiver verificado que o corpo não vaza o array nem escreve tipos errados nele, @SafeVarargs silencia o aviso:
@SafeVarargs
public final <T> List<T> listOf(T... items) {
return java.util.List.of(items); // only reads the items, never stores other types
}Regras:
- Só é legal em métodos que não podem ser substituídos —
static,finalouprivate, além de construtores do estilo record. - A anotação é uma promessa. Se o corpo realmente escreve no array varargs com um tipo errado, o cast no local de chamada pode falhar mais tarde com um confuso
ClassCastException.
Ela tem retenção SOURCE.
@FunctionalInterface
Uma interface funcional é aquela com exatamente um método abstrato — a forma que Runnable, Callable, Comparator e Function compartilham. Expressões lambda e referências de método visam interfaces funcionais. A anotação torna a intenção explícita e pede ao compilador que imponha a regra de método-abstrato-único:
@FunctionalInterface
public interface StringMapper {
String map(String input); // the single abstract method
default StringMapper andThen(StringMapper next) { // default methods are allowed
return s -> next.map(map(s));
}
}Se você adicionar um segundo método abstrato posteriormente, a compilação quebra imediatamente. Sem a anotação, a interface deixaria silenciosamente de ser utilizável como alvo de lambda — algo que um usuário só perceberia no local de chamada.
Como @Override, ela tem retenção SOURCE.
Um exemplo trabalhado: vendo a imposição do compilador
Este programa demonstra as quatro anotações impostas e mostra o que o tempo de execução pode ver depois. As partes interessantes: o método @SafeVarargs compila sem aviso, ao contrário do não anotado; o @FunctionalInterface é refletido pelas regras de contagem de SAM; os valores do elemento @Deprecated aparecem em tempo de execução.
O que tirar da execução:
@FunctionalInterfacefez seu trabalho em tempo de compilação garantindo queStringMapperé um tipo SAM:String::toUpperCasee o lambdas -> s + \"!\"se vincularam a ela sem problemas. Se alguém adicionasse um segundo método abstrato à interface, a compilação falharia e essas expressões parariam de ser resolvidas.- A linha
@Overridefoi a contabilidade que garantiu queChild.describe()realmente substituiParent.describe(). A chamada polimórfica que recai sobre\"child\"confirma isso; se a assinatura tivesse divergido (nome diferente, tipo de retorno diferente), a compilação teria falhado em vez de produzir comportamento incorreto em tempo de execução. @Deprecatedé a única anotação aqui que sobrevive à compilação. A reflexão extraiu com sucessosince=1.4eforRemoval=truedo arquivo de classe. O método em si ainda executou —@Deprecatedavisa, não desabilita.@SafeVarargsremoveu o aviso de "possível poluição do heap" em tempo de compilação enquanto mantinha a chamada type-safe. Note que o método éstatic, portanto satisfaz a regra "não pode ser substituído". Remover a anotação compilaria mas emitiria aviso durante ojavac; adicioná-la em um método não-static, não-final e não-private seria um erro de compilação.@SuppressWarningsnão deixou rastro em tempo de execução — o array de anotações impresso paraparseOrZeroestá vazio. Esse é o objetivo completo da retençãoSOURCE: a anotação faz seu trabalho durante a compilação e depois desaparece, mantendo o arquivo de classe sem sobrecarga.
Outras anotações integradas que vale conhecer
Algumas anotações menos comuns da biblioteca padrão, brevemente:
@SuppressWarnings("preview")— para código que usa recursos de linguagem em prévia (Java 14+).@Native(java.lang.annotation.Native) — marca uma constante que pode ser referenciada a partir de código nativo; usada por ferramentas que geram cabeçalhos JNI.@Generated(javax.annotation.processing.Generated, Java 9+) — adicionada por geradores de código nos arquivos que eles emitem.@Documented,@Retention,@Target,@Inherited,@Repeatable— estas são meta-anotações; cobertas no próximo capítulo.
Você raramente escreverá as três primeiras à mão. As meta-anotações são a porta de entrada para escrever seus próprios tipos de anotação, e a reflexão é como anotações retidas em tempo de execução como @Deprecated são lidas de volta — como o exemplo trabalhado acima mostra.