W3docs

Anotaciones integradas de Java

Anotaciones integradas comunes de Java — @Override, @Deprecated, @SuppressWarnings, @SafeVarargs, @FunctionalInterface.

La biblioteca estándar incluye un pequeño conjunto de anotaciones en java.lang que el compilador trata de forma especial. Ninguna de ellas agrega comportamiento a tu código en tiempo de ejecución; todas son sugerencias para javac (o, en el caso de @Deprecated, para la cadena de herramientas en general). Conocer qué promete cada una — y qué no promete — es la parte práctica de trabajar con anotaciones en el día a día.

Las cinco que verás con mayor frecuencia:

  • @Override — "este método sobreescribe uno de un supertipo."
  • @Deprecated — "no uses esto; está por desaparecer."
  • @SuppressWarnings — "silencia estas advertencias específicas en este ámbito."
  • @SafeVarargs — "mi método varargs no contamina el heap."
  • @FunctionalInterface — "esta interfaz tiene exactamente un método abstracto."

@Override

Colocar @Override en un método le indica al compilador que el método pretende sobreescribir un método de la superclase o implementar un método de interfaz. Si en realidad no sobreescribe nada (porque el nombre está mal escrito, la firma es incorrecta o el padre eliminó el método), javac falla la compilación.

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
}

Detecta el tipo de error que es muy difícil de encontrar en tiempo de ejecución: un método que sobreescribe cuya firma se aleja de la superclase. Siempre escribe @Override en los métodos que pretendes sobreescribir. Tiene retención SOURCE, por lo que desaparece en el momento en que termina la compilación.

@Deprecated

@Deprecated marca algo — clase, método, campo, constructor — como desaconsejado. El compilador advierte en cada punto de uso. Desde Java 9, la anotación acepta dos elementos:

@Deprecated(since = "1.4", forRemoval = true)
public void oldApi() { ... }
  • since documenta cuándo comenzó la deprecación.
  • forRemoval = true es una señal más fuerte: una versión futura planea eliminar la API. El compilador emite una advertencia de eliminación (típicamente más ruidosa que una advertencia de deprecación simple) y la herramienta Javadoc la marca de forma diferente.

A diferencia de @Override, @Deprecated tiene retención RUNTIME — las herramientas de bytecode, los IDEs y la reflexión pueden verla. La etiqueta Javadoc compañera @deprecated (en minúsculas, en un comentario de documentación) lleva la explicación; la anotación activa las herramientas.

@SuppressWarnings

Cuando el compilador tiene razón casi siempre pero se equivoca aquí, @SuppressWarnings silencia una categoría de advertencias dentro del elemento anotado:

@SuppressWarnings("unchecked")
List<String> strings = (List<String>) raw;               // raw cast intentional

@SuppressWarnings({"unchecked", "rawtypes"})
public void uglyButNeeded() { ... }

El elemento string nombra una categoría de advertencia; las comunes son unchecked, rawtypes, deprecation, serial, unused, removal. Los compiladores pueden aceptar otras. Dos reglas para mantener esto limpio:

  1. Anota el ámbito más pequeño posible. Suprime en una variable local o en un único método, nunca en una clase, a menos que cada línea realmente lo necesite.
  2. Acompáñalo con un comentario que explique por qué. Un @SuppressWarnings("unchecked") sin más junto a un cast deja al siguiente lector preguntándose si el cast es realmente seguro.

@SafeVarargs

Un método varargs cuyo tipo de parámetro contiene un parámetro de tipo tiene un problema sutil: en el punto de llamada, el compilador puede tener que crear un array de un tipo genérico, lo cual es inseguro. El compilador advierte sobre esto con el mensaje de "posible contaminación del heap". Si el autor ha verificado que el cuerpo no filtra el array ni escribe tipos incorrectos en él, @SafeVarargs silencia la advertencia:

@SafeVarargs
public final <T> List<T> listOf(T... items) {
  return java.util.List.of(items);                        // only reads the items, never stores other types
}

Reglas:

  • Solo es legal en métodos que no pueden ser sobreescritos — static, final o private, además de constructores estilo record.
  • La anotación es una promesa. Si el cuerpo realmente escribe en el array varargs con un tipo incorrecto, el cast en el punto de llamada puede fallar más tarde con una confusa ClassCastException.

Tiene retención SOURCE.

@FunctionalInterface

Una interfaz funcional es aquella con exactamente un método abstracto — la forma que comparten Runnable, Callable, Comparator y Function. Las expresiones lambda y las referencias a métodos apuntan a interfaces funcionales. La anotación hace explícita la intención y le pide al compilador que aplique la regla de un único método abstracto:

@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));
  }
}

Si más tarde agregas un segundo método abstracto, la compilación falla de inmediato. Sin la anotación, la interfaz dejaría de ser utilizable como destino de una lambda de forma silenciosa — lo que un usuario solo notaría en el punto de llamada.

Al igual que @Override, tiene retención SOURCE.

Un ejemplo práctico: ver la aplicación del compilador

Este programa demuestra las cuatro anotaciones aplicadas y muestra lo que el tiempo de ejecución puede ver después. Los puntos interesantes: el método @SafeVarargs compila donde el no anotado imprime una advertencia; la @FunctionalInterface se refleja mediante las reglas de conteo SAM; los valores del elemento @Deprecated se ven en tiempo de ejecución.

java— editable, runs on the server

Qué extraer de la ejecución:

  • @FunctionalInterface hizo su trabajo en tiempo de compilación garantizando que StringMapper es un tipo SAM: String::toUpperCase y la lambda s -> s + \"!\" se vincularon a ella sin problemas. Si alguien agregara un segundo método abstracto a la interfaz, la compilación fallaría y estas expresiones dejarían de resolverse.
  • La línea @Override fue el bookkeeping que aseguró que Child.describe() realmente sobreescribe Parent.describe(). La llamada polimórfica que aterriza en \"child\" lo confirma; si la firma hubiera divergido (nombre diferente, tipo de retorno diferente), la compilación habría fallado en lugar de producir comportamiento incorrecto en tiempo de ejecución.
  • @Deprecated es la única anotación aquí que sobrevive la compilación. La reflexión extrajo exitosamente since=1.4 y forRemoval=true del archivo de clase. El método en sí aún se ejecutó — @Deprecated advierte, no deshabilita.
  • @SafeVarargs eliminó la advertencia de "posible contaminación del heap" en tiempo de compilación mientras mantenía la llamada con tipos seguros. Nótese que el método es static, por lo que satisface la regla de "no puede ser sobreescrito". Quitar la anotación compilaría pero generaría advertencias durante javac; agregarla en un método no estático, no final y no privado sería un error de compilación.
  • @SuppressWarnings no dejó rastro en tiempo de ejecución — el array de anotaciones impreso para parseOrZero está vacío. Ese es el propósito completo de la retención SOURCE: la anotación hace su trabajo durante la compilación y luego desaparece, manteniendo el archivo de clase sin desorden.

Otras anotaciones integradas que vale la pena conocer

Un puñado de anotaciones menos comunes de la biblioteca estándar, brevemente:

  • @SuppressWarnings("preview") — para código que usa características de lenguaje en vista previa (Java 14+).
  • @Native (java.lang.annotation.Native) — marca una constante que puede ser referenciada desde código nativo; la usan herramientas que generan encabezados JNI.
  • @Generated (javax.annotation.processing.Generated, Java 9+) — agregado por generadores de código en los archivos que emiten.
  • @Documented, @Retention, @Target, @Inherited, @Repeatable — estas son meta-anotaciones; se tratan en el siguiente capítulo.

Raramente escribirás las primeras tres a mano. Las meta-anotaciones son la puerta de entrada a escribir tus propios tipos de anotación, y la reflexión es cómo se leen en tiempo de ejecución las anotaciones con retención RUNTIME como @Deprecated — como muestra el ejemplo práctico anterior.

Práctica

Práctica
Un método se declara como `public <T> T[] toArray(T... values)` y el compilador advierte sobre 'posible contaminación del heap por tipo vararg parametrizado'. El autor examina el cuerpo, confirma que solo escribe elementos de tipo T en el array, y agrega `@SafeVarargs`. ¿Por qué el compilador rechaza la anotación?
Un método se declara como `public <T> T[] toArray(T... values)` y el compilador advierte sobre 'posible contaminación del heap por tipo vararg parametrizado'. El autor examina el cuerpo, confirma que solo escribe elementos de tipo T en el array, y agrega `@SafeVarargs`. ¿Por qué el compilador rechaza la anotación?
Was this page helpful?