Implement (/inherit/~extend) annotation in Kotlin
Asked Answered
S

1

13

In Java I have the possibility to "implement" annotations.

Sample Java annotation:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface JavaClassAnno {
  String[] value();
}

Sample Java "implementation":

class MyAnnotationLiteral 
                  extends AnnotationLiteral<JavaClassAnno> 
                  implements JavaClassAnno { // <--- works in Java
  private String value;

  public MyAnnotationLiteral(String value) {
    this.value = value;
  }
  @Override
  public String[] value() {
    return new String[] { value };
  }
}

Trying to port that to Kotlin doesn't work as it says that the annotation is final and therefore can not be inherited, i.e. the following will not work:

class MyAnnotationLiteral(private val internalValue: String) 
                 : AnnotationLiteral<JavaClassAnno>(), 
                   JavaClassAnno { // <--- doesn't work in Kotlin (annotation can not be inherited)
  override fun value(): Array<String> {
    return arrayOf(internalValue)
  }
}

How do you "implement/extend" annotations the Kotlin way? Could not find any reason why Kotlin differs in that regard to Java. Any hint how to solve that problem or any sources that tell why it is that way are welcome.

The following question contains a use case for this constellation: Dynamically fire CDI event with qualifier with members. Basically you require something like this to narrow down which qualifier should trigger based on its members.

Note that this would also apply to a Kotlin annotation as well as it seems that a Kotlin annotation can not be open and therefore not be implemented/extended too.

What I found so far is rather mentioning @Inherited as a problem:

But I did not find any reason why the annotation is not implementable/inheritable as it is in Java.

I also asked this question now here: https://discuss.kotlinlang.org/t/implement-inherit-extend-annotation-in-kotlin/8916

Update: Finally I found something regarding this design decision, namely the following issue (while I was opening my own issue for it): Annotations inheritance. Either prohibit or implement correctly. As it seems the decision was to "prohibit" it, even though there are no (visible?) comments, discussions or other sources about that decision.

Added the following issue: https://youtrack.jetbrains.com/issue/KT-25947

Samarium answered 31/7, 2018 at 8:34 Comment(11)
Huh. I've never seen a class try to implements an annotation, and I didn't realize that it was even possible. You nearly always apply the annotation to the class.Irony
At least in Java I saw it already several times. One such example is when you try to select an instance in CDI, where you usually use AnnotationLiterals, which are basically "implementations" of annotations. However you may only require such an implementation if you need to supply values or other specific attributes your annotation has.Samarium
Can you point me to actual code that does that? Using an annotation as a qualifier usually doesn't involve implementing it.Irony
Here is an example question. Basically you require something like this to narrow down which qualifier should trigger based on its members.Samarium
Interesting. Reminds me of some of the C++ preprocessor gymnastics from the old days. Just to make sure, is your annotation still written in Java, or is that ported to Kotlin also?Irony
The annotation is still written in Java, but the same would apply to a Kotlin annotation as well (you can't open it).Samarium
Seems like weird kotlin decision then, you can always use proxy classes tooPythagoras
what do you mean by proxy class? For now the only reasonable approach that comes to my mind is to just use the java implementation instead. However that is rather a workaround ;-) and then I still don't know why this language design decision was even taken...Samarium
Proxy class would be even bigger workaround, some random library that use proxy classes for that: github.com/renatoathaydes/javanna/blob/master/src/main/java/com/…Pythagoras
oh dear (I already assumed reflection), but I hope that's not the only best way in Kotlin ;-) hopefully someone can shed some light into this...Samarium
I dont think there is anything more to say, you just need to wait and pray to that issue or use one of that workaroundsPythagoras
E
5

As of Kotlin 1.3, this case is not supported. To create custom instances of annotations one has to resort to Java for now. One of the reasons for this design decision is that making annotations interfaces is too JVM-specific and wouldn't map well to other platforms.

Ernestinaernestine answered 2/9, 2018 at 16:3 Comment(4)
While I understand that decision (and AnnotationLiteral is definitely a rather special case), I wondered also about things like @JvmName already ;-) Are there plans to have something like Kotlin-only on the JVM (without needing @Jvm-annotations)? So maybe like: I am 100% sure (and know what I am doing) and just want to call Kotlin and Java code, but do not want to be able to call Kotlin code from Java (or I wouldn't mind if code called from Java would look ugly because method names might be generated)?Samarium
@Samarium I can't see how your question is related to the topic here. Also, I can't see why you need @JvmName at all if you don't care about Java API. Let's discuss it elsewhereErnestinaernestine
Sorry.. I just jumped from annotations to "too JVM-specific" and reminded myself about name clashes requiring @JvmName and wrote what came to my mind ;-) you are right, that's not really related... sure, where do we want to continue?Samarium
@Samarium I think it's a good discussion for our public Slack or forumErnestinaernestine

© 2022 - 2024 — McMap. All rights reserved.