Memra

Annotations: built-ins and defining your own

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.

NORMAL ~/memra/learn/comp-308/annotations utf-8 LF