Issue with repeated Java 8 annotations

Hi, I’m running into an issue with converting some Java 8 code into Kotlin. I have two annotations:

//Callback.java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Repeatable(Callbacks.class)
public @interface Callback {
    Class<?> value();
}
//Callbacks.java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Callbacks {
    Callback[] value();
}

And I am using them in Java 8 just fine.

//Demo.java
@Callback(Fizz.java)
@Callback(Buzz.java)
public class Demo {
    //......
}

But when I convert that class into a Kotlin class

//Demo.kt
@Callback(Fizz::class)
@Callback(Buzz::class)
class Demo {
    //......
}

I get the following error

Only annotations with SOURCE retention can be repeated on JVM version before 1.8

I’m not sure if this is a Kotlin/Java 8 incompatibility or I just have my project set up wrong. I should also note that the kotlin code above works fine if I only have one @Callback.
Thanks

Basically, the Kotlin compiler for now targets the jdk 1.6 class file format. This means that, on Java, it cannot write multiple annotations to the class file. While conceptually Kotlin supports multiple annotations, until there is proper 1.8 targeting (scheduled for 1.1), it cannot do so because of the output restrictions. Source level annotations are not written to the class file so there is no issue for that.

So, wait until 1.1, use a hack where you use the annotations in Java, or if possible, use a container annotation that contains a list of callbacks (the way this was done before 1.8).

Okay thanks, that makes sense.

Hi,I’m using Kotlin maven plugin version 1.1.1 and I’ve configured jvmTarget param with 1.8, but still have the same error
would you please give some advices?

2 Likes

The same problem here. I am using 1.1.2-2 and configured kotlin with

compileKotlin {
    kotlinOptions {
        jvmTarget = "1.8"
        javaParameters = true
    }
}

And still got the exception.

Same here

@Configuration
@PropertySource("classpath:/default_appinfo.properties")
@PropertySource("classpath:/default_application.properties")
class ApplicationPropertiesConfiguration @Autowired constructor(val environment: Environment) 

fails with
Only annotations with SOURCE retention can be repeated on JVM version before 1.8

We still don’t support repeatable annotations. The relevant issue is here.

The workaround for this issue is to write something like

@MyAnnotations{
  MyAnnotation(<your regular annotation constructor>),
  MyAnnotation(<your regular annotation constructor>)
}

Not a big difference. Still compillator warning is a bit misleading.

2 Likes

We are hitting this at the moment. It’d be great if Kotlin 1.3 could focus on catching up with Java 8 because right now there are still a bunch of tasks where Java is actually better or easier to use than Kotlin - none of them are large, but it seems a shame whenever Java is ahead of Kotlin.

1 Like

FWIW, this issue is serious enough for me to take my project back to Java…I have to interface with a Java framework that makes extensive use of Annotations. The discovery that Kotlin is essentially Java 6 in this respect makes me sad :frowning:

1 Like

I found that this workaround is not universal. It works fin if annotations themselves are defined in java, but does not work if the code is pure kotlin. This is caused by the fact that kotlin hide java parametric Repeatable annotations with its own annotation. Therefore grouping annotation could not be found by java reflections.
As a temporary fix (while there is no repeatable annotation support) I would like to propose to return Java Repeatable(Class) annotation or to make some way of applying it.
For now, it is really inconvenient, I will have to move annotation declarations back to java and I wanted to completely avoid java code in some parts of the project.

The situation is probably not as bad to have a need for going back to Java (see @merrill77), just use the right annotation. Expanding on OP’s example via pure Kotlin declarations:

@Suppress("DEPRECATED_JAVA_ANNOTATION") // suppress deprecation for java.lang.annotation.Repeatable
@java.lang.annotation.Repeatable(Callbacks::class) // define container for compatibility
// standard annotation definition follows:
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
@Repeatable // use Kotlin annotation as well just to be safe (for tools and stuff)
annotation class Callback(val value: KClass<*>)

/** Container annotation for repeating without repeatable support */
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
annotation class Callbacks(vararg val value: Callback)

@Callback(C1::class)
@Callback(C2::class) // errors
class DoesNotSupportRepeated

@Callbacks(
	Callback(C1::class), // don't forget the comma!
	Callback(C2::class) // works
)
class SupportsRepeated

Thanks, I will try it. The problem was that java.lang.annotation.Repeatable was not accessible from my code. I will see if suppression will work.