W3docs

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() { ... }
  • since documenta 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:

  1. 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.
  2. 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, final ou private, 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.

java— editable, runs on the server

O que tirar da execução:

  • @FunctionalInterface fez seu trabalho em tempo de compilação garantindo que StringMapper é um tipo SAM: String::toUpperCase e o lambda s -> 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 @Override foi a contabilidade que garantiu que Child.describe() realmente substitui Parent.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 sucesso since=1.4 e forRemoval=true do arquivo de classe. O método em si ainda executou — @Deprecated avisa, não desabilita.
  • @SafeVarargs removeu 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 o javac; adicioná-la em um método não-static, não-final e não-private seria um erro de compilação.
  • @SuppressWarnings não deixou rastro em tempo de execução — o array de anotações impresso para parseOrZero está vazio. Esse é o objetivo completo da retenção SOURCE: 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.

Prática

Prática
Um método é declarado como `public <T> T[] toArray(T... values)` e o compilador avisa sobre 'possível poluição do heap por tipo vararg parametrizado'. O autor examina o corpo, confirma que ele só escreve elementos do tipo T no array e adiciona `@SafeVarargs`. Por que o compilador rejeita a anotação?
Um método é declarado como `public <T> T[] toArray(T... values)` e o compilador avisa sobre 'possível poluição do heap por tipo vararg parametrizado'. O autor examina o corpo, confirma que ele só escreve elementos do tipo T no array e adiciona `@SafeVarargs`. Por que o compilador rejeita a anotação?
Was this page helpful?