Kotlin data class with use-site target annotation on type and type parameter

Viewed 37

I'm looking for a way to write a Kotlin data class that compiles the annotations to the same bytecode as the following Java class:

public class Pet {

    private List<String> names;

    public @NonNull List<@NonNull String> getNames() {
        return names;
    }

    // constructor, setter, etc. omitted
}

I tried to write the following data class and expected it to put the annotations on the getter's return type and return type parameter:

data class Pet(val names: @get:NonNull List<@get:NonNull String>)

But for each of the annotations I get two compile errors:

  • This annotation is not applicable to target 'undefined target' and use site target '@get'
  • This annotation is not applicable to target 'type usage' and use site target '@get'

The NonNull annotation is from the Java library microprofile-graphql-api:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE_USE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Documented
public @interface NonNull {
}

I run into the same problem when I try to set the annotations on the field:

data class Pet(val names: @field:NonNull List<@field:NonNull String>)

If I remove the use-site targets, then it will compile but it will only compile the annotations into @kotlin.Metadata where they are not visible to a Java library that is not aware of Kotlin.

Is there a way to get the annotations on the type and type parameter of a getter method or field?


Update

The getNames() method in the Java snippet above compiles to

  public getNames()Ljava/util/List;
  @Lorg/eclipse/microprofile/graphql/NonNull;()
  @Lorg/eclipse/microprofile/graphql/NonNull;() : METHOD_RETURN, null
  @Lorg/eclipse/microprofile/graphql/NonNull;() : METHOD_RETURN, 0;

The first annotation is on the method, the second on the method return type (List) and the third on the method return type parameter (String). I'm looking for a solution that results in bytecode with at least the second and third annotations.

The proposed solutions so far and what they compile to

1.

data class Pet(@NonNull val names: List<@NonNull String>)
  public getNames()Ljava/util/List;

This solution does not contain any annotations on the getter method at all in the bytecode. The annotations are only compiled into @kotlin.Metadata where they are not visible to a Java library that is not aware of Kotlin.

2.

data class Pet(@get:NonNull val names: List<@NonNull String>)
  public final getNames()Ljava/util/List;
  @Lorg/eclipse/microprofile/graphql/NonNull;()

This solution only results in an annotation on the method, but not on the method return type or return type parameter.

1 Answers

For the type parameter, you shouldn't use a use-site target at all. There's no use-site target keyword for generic types, I assume because there's no possible ambiguity about what it's targeting at that location.

For the property, you're placing the annotation in the wrong spot. It goes before val. And in this case, you don't need to specify @get, since this particular annotation doesn't support targeting method parameters, and properties have higher priority than fields when annotating them. You would only need to put @field: if you needed to specifically target the field itself.

So, all you need is:

data class Pet(@NonNull val names: List<@NonNull String>)

but you could use this for better clarity:

data class Pet(@get:NonNull val names: List<@NonNull String>)
Related