Runtime @Annotation Processing using Reflection in Kotlin

Arnav Gupta
Coding Blocks
Published in
2 min readAug 18, 2019
Annotations in code are much like annotating in real world text. They give extra meaning to what is being annotated

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.

--

--

Arnav Gupta
Coding Blocks

Swimmer, Coder, Poet, Engineer, Entrepreneur. Co founder of Coding Blocks. Mobile Platform at Zomato