What types are allowed for annotation elements, and what restrictions apply to them?
answer
- primitive, String, Class, enum, annotation, array
- single-dimension arrays only
- values must be compile-time constants
- no List/Object/null
- Class element = type reference, array = list
basics
~10 sAn element's type can be a primitive, String, Class, an enum, another annotation, or a single-dimension array of those. You can't use arbitrary objects, collections like List, or nested arrays.
solid answer
~40 sAnnotation element types are restricted to a closed set evaluated at compile time: the eight primitives, String, Class (optionally bounded, like Class<? extends Number>), any enum type, any other annotation type, or a one-dimensional array of any of those. That's it, no java.util.List, no plain Object, no nested arrays, no generic element types. The reason is that annotation values must be compile-time constants embedded in the class file's constant pool, so they have to be values the compiler can fully resolve and serialize. This is why you'll see frameworks model 'multiple values' as an array element (String[] tags()) and 'a type reference' as Class<?> elements rather than instances. Array values are written with braces: @Tags({"a","b"}), and a single-element array can drop the braces.
go deeper
Lists the common element types (primitive, String, array) and knows arrays use braces.
Recites the full allowed set including Class, enum, and nested annotation, and names what's excluded (List, Object, null, nested arrays).
Explains the compile-time-constant rationale and how Class/array/nested-annotation elements let frameworks model lists and type references within the rules.
Discusses constant-pool serialization, annotation evolution constraints, and patterns (sentinel defaults, marker classes) for designing flexible yet rule-compliant annotation APIs.
## The allowed element types When you declare an annotation element, its return type must be one of a **fixed, closed set**: 1. A **primitive**: `boolean`, `byte`, `short`, `int`, `long`, `char`, `float`, `double`. 2. **`String`**. 3. **`Class`** — optionally bounded, e.g. `Class<?>` or `Class<? extends Serializable>`. This lets the annotation reference a *type* (e.g. which validator class to use). 4. An **enum** type, e.g. `RetentionPolicy`. 5. **Another annotation type** (a nested annotation), e.g. `@Schedule schedule();`. 6. A **single-dimensional array** of any of the above, e.g. `String[]`, `Class<?>[]`, `MyEnum[]`. ## What is NOT allowed - Arbitrary reference types like `Object`, `Integer` (the boxed wrapper), `Date`, or your own classes. - Collection types: `List`, `Set`, `Map`. - **Multi-dimensional arrays** (`String[][]`). - Generic/type-parameterized element declarations. - `null` is never a legal element value, and you cannot set an element to null. ## Why the restriction exists Annotation values are **compile-time constants**. The compiler must be able to fully evaluate each value and store it in the `.class` file (in the constant pool / a RuntimeVisibleAnnotations attribute). A `List` or an arbitrary object can't be serialized as a constant, and its value would depend on runtime construction, which defeats the purpose of metadata fixed at compile time. So the language limits elements to things that *are* constants or references the compiler can resolve: literals, strings, class literals (`Foo.class`), enum constants, and nested annotations. ## How frameworks work within the rules - 'A list of things' becomes an **array element**: `String[] profiles() default {};`. - 'Use this class' becomes a **`Class` element**: `Class<? extends Validator> validatedBy();` — the framework then reflectively instantiates it. - 'A structured sub-config' becomes a **nested annotation element**: `@Retry retry();`. ## Syntax for setting values ```java public @interface Endpoint { String path(); String[] methods() default {"GET"}; Class<?> handler(); } @Endpoint(path = "/users", methods = {"GET", "POST"}, handler = UserHandler.class) class X {} ``` For an array, use braces; a single value array may omit them: `methods = "GET"` is shorthand for `methods = {"GET"}`. A `Class` element is set with a class literal (`UserHandler.class`). ## Common pitfalls - Trying `int[] ()` style or `List<String> tags()` → won't compile. - Trying to default an element to `null` → not allowed; use an empty array or a sentinel value/enum instead. - Using a boxed type `Integer level()` → must be primitive `int`.
- How do frameworks represent a list of values if List isn't allowed?They use a single-dimension array element, e.g. String[] tags(). Setting it uses braces: tags = {"a", "b"}; a single element can drop the braces.
- Why must annotation values be compile-time constants?Because they're stored in the .class file's constant pool as fixed metadata; the compiler must be able to fully evaluate and serialize them, which rules out runtime-constructed objects like collections.
saying these in an interview costs you the question
- Claiming List<String> is a valid element type
- Using a boxed wrapper like Integer instead of int
- Thinking you can default an element to null
- Believing multi-dimensional arrays are allowed