12

I have a framework written in Java that, using reflection, get the fields on an annotation and make some decisions based on them. At some point I am also able to create an ad-hoc instance of the annotation and set the fields myself. This part looks something like this:

public @interface ThirdPartyAnnotation{
    String foo();
}

class MyApp{
    ThirdPartyAnnotation getInstanceOfAnnotation(final String foo)
        {
            ThirdPartyAnnotation annotation = new ThirdPartyAnnotation()
            {
                @Override
                public String foo()
                {
                    return foo;
                }

            }; 
            return annotation;
        }
}

Now I am trying to do the exact thing in Kotlin. Bear in mind that the annotation is in a third party jar. Anyway, here is how I tried it in Kotlin:

class MyApp{
               fun getAnnotationInstance(fooString:String):ThirdPartyAnnotation{
                    return ThirdPartyAnnotation(){
                        override fun foo=fooString
                }
    }

But the compiler complains about: Annotation class cannot be instantiated

So the question is: how should I do this in Kotlin?

3
  • 2
    It seems Kotlin annotations are final and can be neither instantiated nor subclassed. Commented Nov 13, 2015 at 14:05
  • 2
    kotlin can't change the overridability of anything from java Commented Nov 14, 2015 at 9:38
  • Looks like Kotlin indeed won't allow you to implement Java annotation interfaces. My suggestion would be to write an implementation class in Java, then call that from your Kotlin code. Kotlin allows you to write java sources in the same project and handle them without issue. Commented Apr 18, 2023 at 11:10

3 Answers 3

7

You can do this with Kotlin reflection:

val annotation = ThirdPartyAnnotation::class.constructors.first().call("fooValue")

In the case of annotation having no-arg constructor (e.g. each annotation field has a default value), you can use following approach:

annotation class SomeAnnotation(
        val someField: Boolean = false,
)
val annotation = SomeAnnotation::class.createInstance()
Sign up to request clarification or add additional context in comments.

Comments

3

This is the solution I might have found but feels like a hack to me and I would prefer to be able to solve it within the language. Anyway, for what is worth,it goes like this:

class MyApp {
    fun getInstanceOfAnnotation(foo: String): ThirdPartyAnnotation {
        val annotationListener = object : InvocationHandler {
            override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {
                return when (method?.name) {
                    "foo" -> foo
                    else -> FindBy::class.java
                }
            }
        }
        return Proxy.newProxyInstance(ThirdPartyAnnotation::class.java.classLoader, arrayOf(ThirdPartyAnnotation::class.java), annotationListener) as ThirdPartyAnnotation
    }
}

Comments

-1

This should be possible since kotlin 1.6 (see this KEEP)

Issue KT-45395

E.g.

annotation class InfoMarker(val info: String)


fun processInfo(marker: InfoMarker) = ...

fun main(args: Array<String>) {
    if (args.size != 0)
        processInfo(getAnnotationReflective(args))
    else
        processInfo(InfoMarker("default"))
}

However if you are unlucky to be on the lower language level, you need to use the reflective approach like answered by Клаус Шварц. Worse if the annotation constructor have parameters with different JVM names like

    @get:JvmName("k")
    val kind: Int = 1,

then the reflective call (e.g. Metadata::class.constructors.first().call(...)) will fail with a java.lang.NoSuchMethodException: kotlin.Metadata.kind(), then you need to go with the proxy way (Proxy.newProxyInstance) as answered by Claudiu Dumitrescu, /claudiu-dumitrescu) and you may also need to handle default values of the annotation declaration

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.