skip to content

Annotation Parameters

Annotation parameters are limited to compile-time constants: primitives, strings, enums, class literals, other annotations, and arrays of those. Passing a class as MyClass::class is the form worth remembering.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

What types are allowed as parameters of a Kotlin annotation, and which types are NOT allowed?

level: juniorimportance: must knowfreq 60%

answer

  1. Compile-time constants only
  2. Primitives, String, enum, KClass, annotation, arrays
  3. No List/Map, no nullable, no lambdas
  4. Class literal = String::class -> Class in bytecode
  5. Arrays one-dimensional, [..] literal allowed inside annotations

basics

~10 s

Annotation parameters can only be simple compile-time values: numbers, Boolean, Char, String, enums, a class reference, another annotation, and arrays of those. You cannot use arbitrary objects like a List or a date.

solid answer

~40 s

Kotlin restricts annotation parameter types to values known at compile time: the Java primitives and their Kotlin equivalents (Int, Long, Short, Byte, Double, Float, Boolean, Char), String, enum entries, a KClass literal (e.g. String::class, mapped to java.lang.Class in bytecode), other annotation types, and one-dimensional arrays of any of the above. You CANNOT use nullable types, arbitrary class instances, collections like List or Map, functions/lambdas, or generic type parameters. Every argument must be a compile-time constant or expression built only from those constants — so values must come from literals, const val, or enum entries, never a regular val or a function call. This restriction exists because annotations are baked into the .class file metadata and read by reflection or annotation processors before any runtime objects exist.

code

kotlin · 8 lines
kotlin
annotation class Route(
    val path: String,
    val methods: Array<String> = ["GET"],
    val handler: kotlin.reflect.KClass<*>
)

@Route(path = "/users", methods = ["GET", "POST"], handler = String::class)
class UsersController

go deeper

for a junior

Lists the allowed types (primitives, String, enum, KClass, annotation, arrays) and knows collections/nullable are not allowed.

for a middle

Explains the compile-time-constant rule that unifies all the restrictions and uses Array over List.

for a senior

Connects the restriction to class-file metadata storage and how KClass maps to java.lang.Class in bytecode.

for a principal

Reasons about cross-tooling implications (annotation processors / reflection reading constants pre-runtime) when designing annotation-driven APIs.

## What an annotation parameter is An **annotation** is metadata attached to code (`@Deprecated`, `@JvmStatic`, your own `@Audited`). Its **parameters** are the values you pass: `@Deprecated("use foo", level = DeprecationLevel.ERROR)`. Because annotations are stored in the compiled `.class` file and read *statically* (by reflection or by annotation processors at build time), their arguments must be **compile-time constants** — there is no live object graph when they are read. ## Allowed parameter types Kotlin permits exactly these: - **Primitive-backed types**: `Int`, `Long`, `Short`, `Byte`, `Double`, `Float`, `Boolean`, `Char`. - **`String`**. - **Enum classes** — pass an entry, e.g. `DeprecationLevel.ERROR`. - **Class references via `KClass`** — declared as `val type: KClass<*>`, passed as `String::class`. In JVM bytecode this becomes `java.lang.Class`. - **Other annotations** — an annotation type can have a parameter that is itself an annotation, e.g. `@Validated(rule = Rule(min = 1))`. - **Arrays** of any of the above — declared `val tags: Array<String>`, passed `["a", "b"]` (the `[...]` literal is allowed *inside annotations only*) or `arrayOf("a", "b")`. ## NOT allowed - **Nullable types** (`String?`) — annotation params cannot be nullable. - **Arbitrary objects / collections** — `List`, `Map`, `Set`, a data class instance, `LocalDate`, etc. - **Function / lambda types**. - **Generic type parameters** as the parameter type. - **Non-constant expressions** — a regular `val x = computeName()` cannot be passed; only literals, `const val`, and enum entries qualify. ```kotlin enum class Level { LOW, HIGH } annotation class Inner(val n: Int) annotation class Audited( val name: String, // String OK val level: Level, // enum OK val target: kotlin.reflect.KClass<*>, // class literal OK val nested: Inner, // another annotation OK val tags: Array<String>, // array OK val flag: Boolean = false // primitive with default OK ) const val DEFAULT_NAME = "svc" // const val -> usable in annotations @Audited( name = DEFAULT_NAME, level = Level.HIGH, target = String::class, nested = Inner(7), tags = ["x", "y"] ) class Service ``` ## Why the restriction The values are written into class-file metadata. A compiler/processor reading them has no JVM runtime objects to hand, so each value must be reducible to a constant at compile time. That is the single rule behind every item in the allow/deny lists above.

  • Can an annotation parameter be of type List<String>?
    No. Collections are not allowed; use Array<String> instead, which is the supported container type.
  • Can a parameter be nullable, e.g. val name: String??
    No. Annotation parameters cannot be nullable types.

Like writing on a luggage tag: only short fixed text and stamps fit — you can't tie a whole suitcase to the tag.

saying these in an interview costs you the question

  • Claiming you can pass a List or Map as an annotation parameter
  • Saying any object can be used because annotations are 'just classes'
  • Thinking a regular val (not const) can be passed
  • Confusing Array<String> with List<String> for annotation params
  • Believing nullable types are allowed

context

open as a page

What expressions can supply an annotation argument or its default value? Explain the role of const val.

level: middleimportance: must knowfreq 50%

basics

~10 s

Only compile-time constants: literals, enum entries, and values declared with const val. A normal val or a function result won't compile because the value must be known when the code is compiled.

open as a page

How do you pass a class as an annotation argument in Kotlin, and what type does the parameter have? How does it appear in JVM bytecode?

level: middleimportance: should knowfreq 45%

basics

~10 s

You declare the parameter as KClass and pass a class with the ::class syntax, like MyClass::class. In Java bytecode it becomes a java.lang.Class reference, so Java frameworks can read it too.

open as a page

How do array-typed annotation parameters work in Kotlin, including vararg and the array literal syntax? How do they differ from Java's interop expectations?

level: seniorimportance: should knowfreq 35%

basics

~10 s

Annotations can take arrays. You declare Array<String> and pass values with [..] or arrayOf(...). A vararg parameter lets callers pass items without wrapping them, and it shows up as an array to Java.

open as a page

When designing an annotation API, how do the parameter-type restrictions shape your choices (e.g., referencing a strategy class, bounded class literals, avoiding non-constant config)? What patterns work around the limits?

level: principalimportance: nice to knowfreq 20%

basics

~20 s

Because annotations only hold fixed compile-time values, you reference behavior by class (KClass) and let the framework create it, keep config in const values, and push anything dynamic out of the annotation into runtime code.

open as a page