Memra

Annotations: built-ins and defining your own

◈ 8 cards

Standard annotations (@Override, @Deprecated, @SuppressWarnings, @FunctionalInterface) and writing a custom annotation with @Retention and @Target.

Metadata that travels with code

An annotation attaches metadata to a program element — a class, method, field, parameter, etc. It does not change what the code does on its own; it is read by the compiler, by build/test tools, or at runtime via reflection. The syntax is @Name, optionally with element values in parentheses.

### The four built-ins you must know

@Override — declares that a method is intended to override a supertype method. If it does not (wrong name, wrong parameter types), the compiler reports an error. This catches the classic bug where you think you overrode equals(Object) but actually wrote equals(MyType), silently overloading instead.

class Point {
    @Override
    public boolean equals(Object o) {   // correct signature
        // ...
        return true;
    }
}

@Deprecated — marks an element as obsolete. Callers get a compiler warning. Pair it with a Javadoc @deprecated tag explaining the replacement.

@Deprecated
public void oldStart() { /* use start() instead */ }

@SuppressWarnings — silences named compiler warnings for the annotated element. Keep its scope as small as possible (a single statement or method), and document why.

@SuppressWarnings("unchecked")
List<String> names = (List<String>) rawList;

@FunctionalInterface — declares that an interface has exactly one abstract method. The compiler errors if you add a second, protecting interfaces meant to be used as lambda/method-reference targets (like Runnable, Comparator).

@FunctionalInterface
interface Check { boolean test(int n); }

Defining a custom annotation

You declare one with @interface. Its elements look like methods (name + return type), may declare a default, and the special element named value lets callers omit the name:

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
    String value() default "";
    int timeout() default 0;
}

Applying it:

@Test                       // both elements use their defaults
void case1() { }

@Test("login flow")          // "login flow" binds to value (name omitted)
void case2() { }

@Test(value = "timeout case", timeout = 500)  // multiple elements: name each
void case3() { }

Legal element types are restricted: primitives, String, Class, an enum, another annotation, or a one-dimensional array of those.

Meta-annotations: annotations on annotations

Two are essential when you write your own:

  • @Retention — how long the annotation is kept. SOURCE (discarded by the compiler — e.g. @Override), CLASS (kept in the .class file but not loaded at runtime — the default), or RUNTIME (loaded and visible to reflection). You must use RUNTIME if a tool will read it via reflection.
  • @Target — which program elements the annotation may be applied to: ElementType.TYPE, METHOD, FIELD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, etc. Applying it elsewhere is a compile error.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@interface Audited { }

Reading annotations at runtime

Because the AU course pairs annotations with testing, the payoff is reading them reflectively. A RUNTIME-retained annotation is reachable through the reflection API (covered in Module 7):

for (Method m : MyTests.class.getDeclaredMethods()) {
    if (m.isAnnotationPresent(Test.class)) {
        Test t = m.getAnnotation(Test.class);
        System.out.println("running " + m.getName() + ": " + t.value());
        m.invoke(new MyTests());
    }
}

This is exactly how JUnit discovers @Test methods — annotation + reflection, no naming convention required.

@Retentionin the .class filereflection sees ittypical useSOURCEnono@OverrideCLASS (default)yesnobytecode toolsRUNTIMEyesyes@Test discoveryThe default is CLASS, which is not enough for reflection.
Retention is a lifetime, and reflection only reaches the last row. This is the link back to Module 7: an annotation a tool must discover at run time has no choice of policy.
@Test on case2()RUNTIME retentionjavackept in the .class filegetDeclaredMethods()reflection, Module 7isAnnotationPresentkeeps only the marked onesm.invoke(new MyTests())the test runs
How JUnit finds your tests, with no naming convention involved. Break the chain at stage one — leave the retention at its <code>CLASS</code> default — and stage four silently returns <code>false</code> for every method.
NORMAL ~/memra/learn/comp-308/annotations utf-8 LF