Annotations provide a mechanism for attaching metadata to code. To declare an annotation, use the annotation modifier before a class.
annotation class CustomAnnotation
Additional characteristics of an annotation can be specified by annotating the annotation class itself with meta-annotations.
@Targetdefines the types of elements the annotation can be applied to (e.g., class, function, property).@Retentiondetermines if the annotation is stored in the compiled class file and accessible at runtime via reflection (default is true for both).@Repeatablepermits multiple uses of the same annotation on a single element.@MustBeDocumentedindicates the annotation is part of the public API and should be included in generated documentation.
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION,
AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
annotation class CustomAnnotation
Apply annotations using the @ symbol.
@CustomAnnotation class Sample {
@CustomAnnotation fun process(@CustomAnnotation param: Int): Int {
return (@CustomAnnotation 1)
}
}
To annotate a primary constructor, add the constructor keyword and place the annotation before it.
class Sample @Inject constructor(dep: MyDependency) { /*...*/ }
Property accessors can also be annotated.
class Sample {
var data: MyDependency? = null
@Inject set
}
Annotations can have constructors that accept parameters.
annotation class Marked(val reason: String)
@Marked("sample usage") class Demo {}
Permitted parameter types include:
- Primitive types (e.g.,
Int,Long). String.- Classses (e.g.,
Foo::class). - Enums.
- Other annotations.
- Arrays of the above types.
Annotation parameters cannot be nullable, as the JVM does not support storing null as an annotation attribute value.
When an annotation is used as a parameter for another annotation, its name is used without the @ prefix.
annotation class Substitute(val expr: String)
annotation class Outdated(
val msg: String,
val substitute: Substitute = Substitute("")
)
@Outdated("Use '===' instead", Substitute("this === other"))
To specify a class as an annotation parameter, use the Kotlin class reference (KClass). The Kotlin compiler converts this to a Java Class.
import kotlin.reflect.KClass
annotation class AnnotationExample(val arg1: KClass<*>, val arg2: KClass<out Any>)
@AnnotationExample(String::class, Int::class) class ExampleClass
Annotations can be applied to lambda expressions. They target the invoke() method of the lambda's body, which is useful for frameworks like Quasar.
annotation class Pausable
val lambdaFunc = @Pausable { Fiber.delay(10) }
When annotating a property or primary constructor parameter, multiple Java elements are generated. Use use-site target syntax to specify the exact placement.
class DemoClass(
@field:AnnotationExample val first, // Annotates Java field
@get:AnnotationExample val second, // Annotates Java getter
@param:AnnotationExample val third // Annotates constructor parameter
)
To annotate an entire file, place the annotation with the file target at the top level, before the package directive or imports.
@file:JvmName("DemoFile")
package com.example.demo
For multiple annotations on the same target, group them within brackets.
class TestClass {
@set:[Inject VisibleForTesting]
var helper: Helper
}
The complete list of supported use-site targets is:
fileproperty(invisible to Java)fieldget(property getter)set(property setter)receiver(extension function/property receiver)param(constructor parameter)setparam(property setter parameter)delegate(field storing the delegate instance for a delegated property)
To annotate the receiver of an extension function, use this syntax:
fun @receiver:CustomAnnotation String.customExtension() { ... }
If no use-site target is specified, the target is chosen based on the @Target meta-annotation of the annotation being used. If multiple targets are applicable, the first from this list is selected: param, property, field.
Kotlin fully supports Java annotations.
import org.junit.Test
import org.junit.Assert.*
import org.junit.Rule
import org.junit.rules.*
class TestSuite {
@get:Rule val testFolder = TemporaryFolder()
@Test fun basicTest() {
val file = testFolder.newFile()
assertEquals(42, computeAnswer())
}
}
Since Java annotations do not define parameter order, you must use named argument syntax in Kotlin.
// Java annotation
declaration
public @interface SampleAnn {
int intValue();
String textValue();
}
// Kotlin usage
@SampleAnn(intValue = 5, textValue = "test") class Example
For the special value parameter, the name can be omitted.
// Java
public @interface AnnWithSingleValue {
String value();
}
// Kotlin
@AnnWithSingleValue("data") class Item
If the Java value parameter is an array, it becomes a vararg parameter in Kotlin.
// Java
public @interface AnnWithArrayParam {
String[] value();
}
// Kotlin
@AnnWithArrayParam("a", "b", "c") class Container
For other array-type parameters, use array literal syntax (Kotlin 1.2+) or arrayOf().
// Java
public @interface AnnWithArrayMethod {
String[] names();
}
// Kotlin 1.2+
@AnnWithArrayMethod(names = ["x", "y", "z"])
class Box
// Older Kotlin
@AnnWithArrayMethod(names = arrayOf("x", "y", "z"))
class OldBox
Annotation instance values are exposed as properties in Kotlin.
// Java
public @interface SimpleAnn {
int value();
}
// Kotlin
fun examine(ann: SimpleAnn) {
val num = ann.value
}