Annotations in Kotlin are a powerful feature that allows developers to add metadata to their code. This metadata can be used by the compiler, tools, or libraries to provide additional functionality or enforce certain behaviors. Annotations can enhance your code by making it more expressive, reducing boilerplate, and enabling advanced features such as code generation and runtime processing.
with hands-on learning.
get the skills and confidence to land your next move.
In this guide, we will explore the concept of annotations in Kotlin. We will understand how to define and use standard and custom annotations, learn about meta-annotations, and see practical examples of how annotations can simplify and enhance your code.
Basics of Annotations
Defining Annotations
Annotations are defined using the @ symbol followed by the annotation name. Annotations can be applied to various elements in your code, such as classes, functions, properties, and parameters.
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class MyAnnotationIn this example, we define a custom annotation MyAnnotation that can be applied to functions.
Applying Annotations
Annotations are applied by placing them before the target element.
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class MyAnnotation
@MyAnnotation
fun annotatedFunction() {
println("This function is annotated")
}
fun main() {
annotatedFunction()
}Here, MyAnnotation is applied to annotatedFunction, indicating that this function has the specified metadata.
Standard Kotlin Annotations
@Deprecated
The @Deprecated annotation marks a class, function, or property as deprecated, indicating that it should no longer be used.
@Deprecated("Use newFunction() instead", ReplaceWith("newFunction()"))
fun oldFunction() {
println("This function is deprecated")
}
fun newFunction() {
println("This is the new function")
}
fun main() {
oldFunction() // Warning: Use newFunction() instead
newFunction()
}In this example, oldFunction is marked as deprecated, suggesting newFunction as a replacement.
@JvmOverloads
The @JvmOverloads annotation generates multiple overloaded versions of a function with default parameter values.
class MyClass {
@JvmOverloads
fun greet(message: String = "Hello", name: String = "World") {
println("$message, $name!")
}
}
fun main() {
val myClass = MyClass()
myClass.greet() // Output: Hello, World!
myClass.greet("Hi") // Output: Hi, World!
myClass.greet("Hi", "John") // Output: Hi, John!
}Here, @JvmOverloads generates overloaded versions of greet with different combinations of default parameters.
@JvmStatic
The @JvmStatic annotation makes a method in a companion object or object directly callable from Java code as a static method.
class MyClass {
companion object {
@JvmStatic
fun staticMethod() {
println("This is a static method")
}
}
}
fun main() {
MyClass.staticMethod()
}In this example, @JvmStatic makes staticMethod callable as a static method in Java.
Custom Annotations
Creating Custom Annotations
Custom annotations are defined using the annotation class keyword. You can specify the target and retention policy using meta-annotations.
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class CustomAnnotation(val message: String)Here, CustomAnnotation is defined with a message parameter and can be applied to classes and functions.
Using Custom Annotations
Custom annotations are applied just like standard annotations.
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class CustomAnnotation(val message: String)
@CustomAnnotation("This is a custom annotation")
class AnnotatedClass
@CustomAnnotation("This function is annotated")
fun annotatedFunction() {
println("Function with custom annotation")
}
fun main() {
val annotation = AnnotatedClass::class.annotations.find { it is CustomAnnotation } as CustomAnnotation?
println(annotation?.message) // Output: This is a custom annotation
annotatedFunction()
}In this example, CustomAnnotation is applied to a class and a function. The annotation value is retrieved using reflection.
Meta-Annotations
@Target
The @Target meta-annotation specifies the possible elements an annotation can be applied to.
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class MyAnnotationHere, MyAnnotation can be applied to classes and functions.
@Retention
The @Retention meta-annotation specifies whether the annotation is stored in the compiled class files and whether it is visible through reflection at runtime.
@Retention(AnnotationRetention.RUNTIME)
annotation class MyAnnotationIn this example, MyAnnotation is retained at runtime, making it accessible through reflection.
@Repeatable
The @Repeatable meta-annotation allows an annotation to be applied multiple times to the same element.
@Repeatable
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class MyRepeatableAnnotation(val message: String)
@MyRepeatableAnnotation("First instance")
@MyRepeatableAnnotation("Second instance")
fun repeatableFunction() {
println("Function with repeatable annotations")
}
fun main() {
repeatableFunction()
}Here, MyRepeatableAnnotation is applied twice to the same function.
Practical Examples
Validation Annotations
Custom annotations can be used for validation purposes.
import kotlin.reflect.full.memberProperties
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
annotation class NotEmpty(val message: String = "Field cannot be empty")
data class User(
@NotEmpty val name: String,
@NotEmpty val email: String
)
fun validate(obj: Any): List<String> {
val errors = mutableListOf<String>()
val kClass = obj::class
for (property in kClass.memberProperties) {
for (annotation in property.annotations) {
if (annotation is NotEmpty && (property.getter.call(obj) as? String).isNullOrEmpty()) {
errors.add("${property.name}: ${annotation.message}")
}
}
}
return errors
}
fun main() {
val user = User("", "example@example.com")
val errors = validate(user)
errors.forEach(::println) // Output: name: Field cannot be empty
}In this example, NotEmpty is a custom validation annotation applied to fields in the User class. The validate function checks for empty fields and collects validation errors.
Logging Annotations
Custom annotations can be used for logging purposes.
import kotlin.reflect.full.declaredFunctions
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class LogExecution
class MyClass {
@LogExecution
fun doWork() {
println("Doing work...")
}
}
fun logMethodExecution(obj: Any) {
val kClass = obj::class
for (function in kClass.declaredFunctions) {
if (function.annotations.any { it is LogExecution }) {
println("Logging execution of ${function.name}")
function.call(obj)
}
}
}
fun main() {
val myClass = MyClass()
logMethodExecution(myClass) // Output: Logging execution of doWork Doing work...
}Here, LogExecution is a custom annotation applied to a method. The logMethodExecution function logs the execution of annotated methods.
Conclusion
Annotations in Kotlin provide a powerful mechanism to add metadata to your code, enabling additional functionality and enforcing certain behaviors. By understanding and utilizing standard and custom annotations, you can enhance your code with validation, logging, and other advanced features. Meta-annotations allow you to define the scope and behavior of your annotations, making them more flexible and powerful.
Additional Resources
To further your understanding of Kotlin annotations and their capabilities, consider exploring the following resources:
- Kotlin Documentation: The official documentation for Kotlin. Kotlin Documentation
- Kotlin by JetBrains: Learn Kotlin through official JetBrains resources. Kotlin by JetBrains
- Kotlin for Android Developers: A comprehensive guide to using Kotlin for Android development. Kotlin for Android Developers
- Kotlin Koans: Interactive exercises to learn Kotlin. Kotlin Koans
- KotlinConf Talks: Watch talks from the Kotlin conference. KotlinConf Talks
By leveraging these resources, you can deepen your knowledge of Kotlin and enhance your ability to develop efficient and maintainable applications.




