Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the compatibility risks of replacing METHOD/FIELD/etc. targets with TYPE_USE

I have a Java package that contains annotations used by external clients. The package appeared before Java 8, so historically these annotations have targets ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE. Now the package requires at least Java 8 version. Semantically the annotations in the package are applicable to the types, so e.g. when the method is annotated, the annotation effectively applies to the method return type. Now clients want to annotate generic type arguments as well (e.g. List<@MyAnnotation String>). Since we dropped the support of Java 7 and below, it seems quite natural to set the annotation target to ElementType.TYPE_USE and, to reduce ambiguity, remove the existing targets.

Here's the question: are there any compatibility risks for existing clients when replacing ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE targets with TYPE_USE? Is it possible that the existing code will stop compiling? What about binary compatibility? May it cause any runtime problems if the class-files compiled before the change are used together with newer annotations pack at runtime?

Annotations' retention policy is CLASS if this matters.

like image 651
Tagir Valeev Avatar asked Jan 30 '20 03:01

Tagir Valeev


1 Answers

There are a number of source compatibility issues that may arise during this change:

  • Methods with void return type cannot be annotated anymore. @MyAnnotation void method() {} is compilable with ElementType.METHOD target but not compilable with TYPE_USE target.
  • When a qualified type is used in the source code, the TYPE_USE annotation must appear after the qualifier. E.g. void method(@MyAnnotation OuterClass.InnerClass param) {} is a valid code with ElementType.PARAMETER target, but should be updated to void method(OuterClass.@MyAnnotation InnerClass param) {} after changing to TYPE_USE.
  • Annotation applied to arrays will have a different meaning. E.g. before the migration @MyAnnotation String[] getData(); annotates the method getData, so the annotation clients likely assume that annotation is applied to the return type (an array of strings). After the migration, the same code means that the annotation is applied to the array component (string). This might cause a behavioral change, depending on the annotation semantics and how it's processed by clients. To preserve the meaning, clients should update such a code to String @MyAnnotation [] getData();.
  • Such a change makes all usages of the annotations in Kotlin code invalid. In Kotlin, there's strict syntactical difference between TYPE_USE annotations and others. E.g. a PARAMETER annotation must be used as fun(@MyAnnotation param : String) {...}. This is incorrect for TYPE_USE annotation, which must be used as fun(param : @MyAnnotation String) {...}. If your clients use Kotlin they will have to fix every single annotation use.
  • Such a change disallows using your annotations in Groovy code. Currently, as Groovy documentation says, Groovy does not support the TYPE_PARAMETER and TYPE_USE element types which were introduced in Java 8. If your clients use Groovy they won't be able to use your annotation package anymore.

No problems should arise at runtime though. As annotations' retention policy is CLASS (not RUNTIME), while the annotations present in class files they are ignored by the runtime. It's not necessary to add your annotation pack to the classpath at all.

like image 193
Tagir Valeev Avatar answered Oct 17 '22 18:10

Tagir Valeev