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

Roland
  • 22,259
  • 4
  • 57
  • 84
  • 3
    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. – chrylis -cautiouslyoptimistic- Jul 31 '18 at 08:37
  • 1
    At least in Java I saw it already several times. One such example is when you try to [select an instance in CDI](https://docs.jboss.org/cdi/api/2.0/javax/enterprise/inject/Instance.html#select-java.lang.annotation.Annotation...-), where you usually use `AnnotationLiteral`s, 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. – Roland Jul 31 '18 at 08:40
  • 1
    Can you point me to actual code that does that? Using an annotation as a qualifier usually doesn't involve *implementing* it. – chrylis -cautiouslyoptimistic- Jul 31 '18 at 08:42
  • 2
    [Here is an example question](https://stackoverflow.com/questions/6925010/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. – Roland Jul 31 '18 at 08:48
  • 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? – chrylis -cautiouslyoptimistic- Jul 31 '18 at 08:51
  • 2
    The annotation is still written in Java, but the same would apply to a Kotlin annotation as well (you can't open it). – Roland Jul 31 '18 at 08:55
  • Seems like weird kotlin decision then, you can always use proxy classes too – GotoFinal Aug 07 '18 at 06:23
  • 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... – Roland Aug 07 '18 at 06:28
  • Proxy class would be even bigger workaround, some random library that use proxy classes for that: https://github.com/renatoathaydes/javanna/blob/master/src/main/java/com/athaydes/javanna/Javanna.java#L95 – GotoFinal Aug 07 '18 at 06:52
  • 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... – Roland Aug 07 '18 at 06:57
  • I dont think there is anything more to say, you just need to wait and pray to that issue or use one of that workarounds – GotoFinal Aug 07 '18 at 07:11

1 Answers1

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.

Andrey Breslav
  • 24,795
  • 10
  • 66
  • 61
  • 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)? – Roland Sep 02 '18 at 16:21
  • @Roland 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 elsewhere – Andrey Breslav Sep 02 '18 at 19:25
  • Sorry.. I just jumped from annotations to "too JVM-specific" and reminded myself about [name clashes requiring `@JvmName`](https://stackoverflow.com/questions/42916801/how-does-erasure-work-in-kotlin) and wrote what came to my mind ;-) you are right, that's not really related... sure, where do we want to continue? – Roland Sep 02 '18 at 20:37
  • @Roland I think it's a good discussion for our public Slack or forum – Andrey Breslav Sep 11 '18 at 03:54