skip to content

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