Kotlin annotations provide a structured way to attach metadata to your code. This metadata can offer guidance to compilers, IDEs, and even runtime environments. Annotations play a vital role in enhancing the behavior and readability of code, especially in large-scale Android projects.
Understanding Annotations
In Kotlin, annotations are created using the annotation
keyword, allowing you to attach metadata to elements like classes, functions, properties, and more. Here’s a simple example:

An annotation like this can be used to decorate different code elements, providing useful metadata for compilers and tools. When you’re building Android projects, annotations such as @Inject or @Parcelize become essential to streamline functionality.
Advanced Annotation Features with Meta-Annotations
In Kotlin, annotations can be further customized using meta-annotations like:
- @Target – Specifies which code elements (e.g., classes, functions) the annotation can be applied to.
- @Retention – Determines whether the annotation is available at runtime via reflection or just during compilation.
- @MustBeDocumented – Marks the annotation as part of the public API, ensuring it appears in generated documentation.
Here’s how you can combine these meta-annotations:
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
annotation class Fancy
For a deeper dive into Kotlin annotations and their advanced uses, you can explore this guide on null-safety in Kotlin and how annotations play a role.
Practical Usage of Annotations
You can apply an annotation to various elements in Kotlin:
@Fancy class Example {
@Fancy fun method(@Fancy param: Int): Int {
return (@Fancy 1)
}
}
This example showcases how annotations can be used across classes, functions, parameters, and even expressions, providing developers with the flexibility to tailor their code’s behavior based on context.
Constructor Annotations
If you need to annotate a class constructor, use the constructor
keyword to mark it appropriately:
class Example @Inject constructor(dependency: Dependency) { ... }
You can also annotate property accessors:
class Example {
var dependency: Dependency? = null
@Inject set
}
Using Parameters in Annotations
Kotlin allows annotations to take parameters, enabling more dynamic metadata attachment. Supported parameter types include:
- Primitive types (e.g.,
Int
,Long
) - Strings
- Classes (e.g.,
MyClass::class
) - Enums
- Other annotations
- Arrays of the above types
Here’s an example:
annotation class Special(val reason: String)
@Special("Example") class ExampleClass
If you’re working on an Android project, you’ll encounter this pattern often, especially when dealing with libraries like Jetpack Compose or Dagger Hilt.
Annotations for Constructors and Lambdas
In Kotlin, you can use annotations on constructors and lambdas. When annotating constructors, the constructor
keyword is used, as seen earlier. For lambdas, annotations are applied to the invoke()
method of the lambda body. This is useful for concurrency frameworks like Quasar, which rely on annotations for concurrency control:
annotation class Suspendable
val myLambda = @Suspendable { Fiber.sleep(10) }
For more advanced scenarios involving lambdas and asynchronous programming, check out this guide on concurrency with Kotlin coroutines.
Site-Targeted Annotations
Annotations in Kotlin can also target specific elements in your code, such as fields, getters, setters, or constructor parameters. To achieve this, Kotlin uses the following syntax:
class Example(@field:Ann val foo, @get:Ann val bar, @param:Ann val baz)
This allows developers to apply annotations precisely where they need them, ensuring better control over behavior. For example, when building Android apps that use WebView, you might annotate a parameter related to the cookie retrieval process.
Use-Site Target List
The full list of Kotlin’s supported use-site targets includes:
- file – Targets the file itself
- property – Targets properties (not visible to Java)
- field – Targets fields
- get – Targets property getters
- set – Targets property setters
- receiver – Targets extension function or property receivers
- param – Targets constructor parameters
- delegate – Targets delegate properties
Site-targeted annotations are commonly used when integrating libraries like Jetpack Compose or Retrofit in Android projects.
Annotations with Java Compatibility
Kotlin annotations are fully compatible with Java annotations, allowing you to leverage existing Java libraries in your Kotlin projects seamlessly. For example:
import org.junit.Test
import org.junit.Assert.*
import org.junit.Rule
import org.junit.rules.*
class ExampleTest {
@get:Rule val tempFolder = TemporaryFolder()
@Test fun testExample() {
val file = tempFolder.newFile()
assertEquals(42, getAnswer())
}
}
This compatibility is especially useful when integrating tools like JUnit or Espresso into your Kotlin-based Android projects.
Repeatable Annotations
Just like in Java, Kotlin supports repeatable annotations. These allow the same annotation to be applied to a code element multiple times:
@Repeatable
annotation class Tag(val name: String)
@Tag("Example1")
@Tag("Example2")
class MyClass
With repeatable annotations, Kotlin reduces boilerplate code while enabling more flexible metadata assignment.
Conclusion
Kotlin annotations provide powerful tools to enhance and structure your code. From targeting specific elements to integrating with Java libraries, annotations improve flexibility and maintainability in Android projects. When working with libraries like Koin or GraphQL, mastering annotations can make a significant difference in how efficiently your code runs.
Did you like this article?
You can subscribe to my newsletter below and get updates about my new articles.