Passing annotations as arguments


#1

Is it possible to pass annotation as an argument of a function?
I wanted to invoke every method annotated with annotation XY and in other situation every method annotated with YZ.

I have a following function:

fun process(annotation: Annotation, eyeTrackingContext: EyeTrackingContext) {
    subscriberSet.forEach { instance ->
        run {
            instance::class.declaredMemberFunctions.forEach { f ->
                run {
                    when (annotation) {
                        is OnFaceAppeared -> {
                            f.findAnnotation<OnFaceAppeared>()?.let {
                                f.call(instance, eyeTrackingContext)
                            }
                        }
                        is OnFaceLost -> {
                            f.findAnnotation<OnFaceLost>()?.let {
                                f.call(instance, eyeTrackingContext)
                            }
                        }
                        is OnContextUpdate -> {
                            f.findAnnotation<OnContextUpdate>()?.let {
                                f.call(instance, eyeTrackingContext)
                            }
                        }
                        else -> {
                            Log.v(TAG, "Method does not specify any allowed annotation.")
                        }
                    }
                }
            }
        }
    }
}

and I would like to perform different operation based on which annotation is passed as an argument. Is there any change to do that in Kotlin? Or I’d have to create N functions with a slightly different name that would call desired function?

To explain the function a bit:

  • it goes through every class that subscribed for notifications
  • then it checks if there are any methods annotated with custom annotations (OnFaceAppeared, OnFaceLost, OnContextUpdate)
  • if it finds desired annotation (is just not null), then it calls function
  • otherwise does nothing

#2

Have you actually tried it? It should be working since under the covers Annotation is just a regular class. It does work in Java so assume it will work in Kotlin.


#3

There’s probably some missing knowledge on my side, but I wanted to call it in this way:

 EyeTrackerAnnotationProcessor.process(OnFaceAppeared::class, eyeContext)

and IDEA immedately shows warning. And I cannot pass instance of Annotation class as I don’t have any.


#4

Your function takes annotation object, but you are passing class to it. You either need to pass the class, or modify function to take ‘KClass’.


#5

As far as I know, you can not instantiate any Annotation so you will need to pass the annotation’s KClass as an argument.

fun <T : Annotation> process(annotation: KClass<T>, eyeTrackingContext: EyeTrackingContext) {
    subscriberSet.forEach { instance ->
        run {
            instance::class.declaredMemberFunctions.forEach { f ->
                run {
                    when (annotation) {
                        OnFaceAppeared::class -> {
                            f.findAnnotation<OnFaceAppeared>()?.let {
                                f.call(instance, eyeTrackingContext)
                            }
                        }
                        OnFaceLost::class -> {
                            f.findAnnotation<OnFaceLost>()?.let {
                                f.call(instance, eyeTrackingContext)
                            }
                        }
                        OnContextUpdate::class -> {
                            f.findAnnotation<OnContextUpdate>()?.let {
                                f.call(instance, eyeTrackingContext)
                            }
                        }
                        else -> {
                            Log.v(TAG, "Method does not specify any allowed annotation.")
                        }
                    }
                }
            }
        }
    }
}

And to call the function:

EyeTrackerAnnotationProcessor.process(OnFaceAppeared::class, eyeContext)

#6

Thank you, this is exactly what I ended with as @darksnake recommended.