skip to content

How do you declare a custom annotation in Java, and what makes its syntax different from a regular interface?

level: juniorimportance: must knowfreq 70%

answer

  1. @interface, not interface
  2. elements = no-arg, no-body methods
  3. implicitly extends Annotation
  4. add @Retention + @Target
  5. restricted return types only

basics

~10 s

You declare a custom annotation with the @interface keyword instead of interface. Inside, each method (with no body) becomes a configurable element you can set when you use the annotation, like @MyAnno(name = "x").

solid answer

~40 s

A custom annotation is declared with @interface, which tells the compiler this is an annotation type rather than a normal interface. Each member is written as a parameterless, no-body method, e.g. String name();. These methods define the annotation's 'elements' (its attributes). When you apply the annotation you set those elements as name=value pairs: @MyAnno(name = "orders"). Under the hood an annotation type is a special interface that implicitly extends java.lang.annotation.Annotation, but you never implement it yourself, you can't extend other interfaces, and its methods can't take parameters or throw checked exceptions. You typically also add meta-annotations like @Retention and @Target to control where it can go and whether it survives to runtime.

go deeper

for a junior

Knows to use @interface and that elements are set as name=value pairs when applying the annotation.

for a middle

Explains the implicit Annotation supertype, the no-parameter/no-body element rule, and why @Retention/@Target are needed.

for a senior

Articulates the full set of element constraints (allowed return types, no generics/throws/inheritance) and ties retention to reflective use.

for a principal

Frames custom annotations as the contract layer for declarative frameworks and can reason about evolution/compatibility of an annotation's element set over time.

## What is an annotation? An **annotation** is metadata you attach to Java code (classes, methods, fields, parameters, etc.). It does not change what the code does by itself; instead, tools, frameworks, the compiler, or your own code read it later and act on it. Examples you've seen: `@Override`, `@Deprecated`, `@SuppressWarnings`. A **custom annotation** is one you define yourself. ## The `@interface` keyword You declare an annotation type with the keyword `@interface` (the `@` immediately followed by `interface`, no space): ```java public @interface Audited { } ``` This looks almost like an interface, and that's intentional: under the hood an annotation type **is** a special kind of interface. The compiler makes every annotation type implicitly extend `java.lang.annotation.Annotation`. You never write `extends`, you never implement it manually, and you cannot make it extend any other interface. ## Elements (the 'attributes') Inside the body you declare **elements**. An element is written exactly like an abstract method: a return type, a name, empty parentheses, and a semicolon, but **no body and no parameters**: ```java public @interface Audited { String action(); // element named 'action', type String int level(); // element named 'level', type int } ``` These 'methods' are not behavior; they are the names and types of the values you can attach. When you *use* the annotation you supply those values as `name = value` pairs: ```java @Audited(action = "DELETE", level = 2) public void deleteUser(long id) { ... } ``` ## Rules that distinguish it from a normal interface 1. Elements **cannot have parameters** (`String name(String x);` is illegal). 2. Elements **cannot have type parameters / be generic**. 3. Elements **cannot declare `throws`** of a checked exception. 4. The **return type is restricted** to a fixed set: primitives (`int`, `boolean`, ...), `String`, `Class` (or a bounded `Class<? extends X>`), an `enum` type, another annotation type, or a **single-dimension array** of any of those. No arbitrary objects, no `List`, no nested arrays. 5. An annotation type **cannot extend** another interface or annotation; there is no inheritance of elements. ## Meta-annotations you almost always add An annotation declaration is usually itself annotated with **meta-annotations**: - `@Retention(RetentionPolicy.RUNTIME)` decides whether the annotation is discarded by the compiler, kept in the class file, or also visible via reflection at runtime. - `@Target({ElementType.METHOD, ElementType.TYPE})` restricts which program elements it may be placed on. Without `@Retention(RUNTIME)` you cannot read the annotation reflectively at runtime; the default retention is `CLASS` (kept in the .class file but not loaded for reflection). ## Why it matters Custom annotations are the foundation of declarative frameworks (Spring's `@Transactional`, JPA's `@Entity`, validation's `@NotNull`). Declaring one correctly, the right elements, types, retention, and target, is what lets a framework or your own reflective/annotation-processing code discover and act on it.

  • Why does an annotation element look like a method with no body?
    Because an annotation type is compiled to a special interface; the 'methods' are the accessor signatures for its values. The compiler synthesizes the implementation, so you never write a body.
  • What happens if you omit @Retention?
    The default is RetentionPolicy.CLASS: the annotation is stored in the .class file but not available via reflection at runtime, so runtime code can't read it.

saying these in an interview costs you the question

  • Thinking you implement an annotation like a normal interface
  • Declaring an element with a parameter, e.g. String name(String x)
  • Forgetting @Retention(RUNTIME) and expecting reflection to see it
  • Believing annotations change runtime behavior on their own

context