Runtime @Annotation Processing using Reflection in Kotlin
Let us take a look at Annotations in Kotlin, and start with a simple example of processing annotations at runtime using JVM’s reflection framework.
NOTE: This is shown in Kotlin, but is specific to how Kotlin/JVM works, and is not applicable to Kotlin/JS or Kotlin/Native.
Creating an Annotation
First of all, we create annotations in Kotlin using the keywords annotation class
.
Here is a newly created Annotation called NiceVar
@Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.FIELD)
annotation class NiceVar
@Rentention
and @Target
are called meta annotations. That is to say - they are annotations that are used to annotate other annotations. Target defines what kind of elements (class, field, method etc) can be annotated.
Note that we are using a Retention policy of RUNTIME
. If we use SOURCE
, then that annotation stays in our source, is useful for our compiler, but gets removed from the runtime.
Annotating some fields
Let us now create a class, and annotate some of its fields with this new annotation we just created.
data class SampleClass (
var a: Int,
var b: String,
@NiceVar var c: Int,
@NiceVar var d: String
)
Using the annotations at runtime
Finally I am going to create a function called printNiceVars()
that will print all the fields of an object that had been annotated with @NiceVar
This is how that is going to look like.
fun main() {
val obj1 = SampleClass(10, "foo", 20, "bar")
printAllVars(obj1)
printNiceVars(obj1)
}
fun printAllVars(obj: Any) {
println(obj.toString())
}
fun printNiceVars(obj: Any) {
obj.javaClass.declaredFields.forEach { f ->
if (f.isAnnotationPresent(NiceVar::class.java)) {
if (f.isAccessible || f.trySetAccessible())
println("${f.name} ${f.get(obj)}")
}
}
}
As you can see, we iterate through all the fields of the object, and then find out if @NiceVar
annotation is present on it or not. If that is the case, we also need to make the field accessible (i.e. if it was private, we cannot directly access it here). And then we print it.
Future Reading
While this gives you an idea how to create and use annotations, this uses reflection, and this is not the best solution performance and secutity wise. In fact most popular libraries like Retrofit, or in Android, DataBinding or Glide use a more powerful strategy called code generation which does the job of annotation processing at compile time itself and the annotations need to stay till run time.
We can take a look at how that works in a future article.
Originally published at http://github.com.