What is the difference between field: array of annotation vs separate annotations

Viewed 43

Is there a difference while expressing a set of annotations in two different ways? Will the result be the same? Example:

class Sample {

    @Inject
    @CustomInfo
    lateinit var fieldA: Any

    @field:[
        Inject
        CustomInfo
    ]
    lateinit var fieldB: Any
}

Annotations:

@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class CustomInfo

and:

@Target({ METHOD, CONSTRUCTOR, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Inject {}

Will there be the same output?

The decompiled Java code produced:

public final class Sample {

   @Inject
   public Object fieldA;
   @Inject
   @CustomInfo
   public Object fieldB;

   /** @deprecated */
   // $FF: synthetic method
   @CustomInfo
   public static void getFieldA$annotations() {
   }
}
1 Answers

Both ways of writing the annotations apply @Inject and @CustomInfo onto the line lateinit var fieldA: Any.

However, there are multiple Java elements that are generated by this single Kotlin declaration - a backing field, a getter and setter - as well as the Kotlin property itself. The second way:

@field:[
    Inject
    CustomInfo
]
lateinit var fieldB: Any

explicitly states that you are applying these annotations to the underlying backing field.

The first way does not do that:

@Inject
@CustomInfo
lateinit var fieldA: Any

Instead, Kotlin uses its own documented rules (explained below) to decide which thing to apply your annotations on.

For each annotation, Kotlin finds all the targets available in the current context that the annotation can be applied on.

  • If there is a parameter, it applies the annotation to the parameter.
  • Otherwise, if there is a property, it applies the annotation to the property.
  • Otherwise, if there is a field, it applies the annotation to the field.

Now looking at your two annotations, Inject is a Java annotation that can only be applied to fields, so for Inject, it doesn't which way you write it.

On the other hand, CustomInfo lacks a Target annotation, and so can be applied to both a property and a field (among other things). Kotlin would choose to apply it to the property if you do it this way:

@Inject
@CustomInfo
lateinit var fieldA: Any

Java code would not be able to find this annotation on the field! Looking at the decompiled code, it appears that the Kotlin compiler generates an additional, deprecated, synthetic method with @CustomInfo on it.

Side note: the [] doesn't actually create an "array". It's just some delimiters so that the parser (and the person reading the code) knows more easily which annotations are associated with @field: and which are not.

See also: Annotation Use-site Targets

Related