[type-annos-observers] annotated type of array-typed varargs parameter
Markus Keller
markus_keller at ch.ibm.com
Fri Apr 4 14:14:26 UTC 2014
We've found a problem with the definition of the varargs syntax on array
types. The methods "var" and "arr" below should intuitively have the same
formal parameter type "Object @A [] @B []", but that clashes with the
order of annotations on array dimensions:
interface Test {
void var(Object @A [] @B ... pVar);
void arr(Object @A [] @B [] pArr);
}
@Target(ElementType.TYPE_USE) @interface A { }
@Target(ElementType.TYPE_USE) @interface B { }
The clarification for 8.4.1 in
https://bugs.openjdk.java.net/browse/JDK-8038881 says: "If the formal
parameter is a variable arity parameter, then the declared type is an
array type whose component type is UnannType."
In "var", the UnannType is "Object @A []", so the type of the parameter is
actually "Object @B [] @A []", according to the annotation order given in
9.7.4.
In JDK 1.8.0-b132, reflection uses the intuitive order. The snippet below
prints:
var: class java.lang.Object @A [] @B []
arr: class java.lang.Object @A [] @B []
That's incorrect according to JLS8. java-annotation-design.html doesn't
address this problem (just says "The varargs syntax “...” is treated
analogously to array brackets").
I don't see a good solution.
a) Say JLS8 is right. => We lose the simple transformation that turns a
vararg into a normal array dimension by changing ... into [].
b) Say the above transformation must be valid. => The annotation in front
of the ... no longer applies to the outermost array type, which is also
strange, because that's the array that is constructed by the compiler for
a variable arity method invocation.
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.stream.*;
@Target(ElementType.TYPE_USE) @Retention(RetentionPolicy.RUNTIME)
@interface A {}
@Target(ElementType.TYPE_USE) @Retention(RetentionPolicy.RUNTIME)
@interface B {}
public class Snippet {
void var(Object @A [] @B ... pVar) { }
void arr(Object @A [] @B [] pArr) { }
public static void main(String[] args) throws Exception {
printParamType(Snippet.class.getDeclaredMethod("var",
Object[][].class));
printParamType(Snippet.class.getDeclaredMethod("arr",
Object[][].class));
}
private static void printParamType(Method method) {
System.out.printf("%-5s %s\n", method.getName() + ":",
toString(method.getAnnotatedParameterTypes()[0]));
}
private static String toString(AnnotatedType annotatedType) {
StringBuilder sb = new StringBuilder();
while (annotatedType instanceof AnnotatedArrayType) {
sb.append(" ")
.append(toString(annotatedType.getAnnotations()))
.append("[]");
annotatedType = ((AnnotatedArrayType) annotatedType)
.getAnnotatedGenericComponentType();
}
Type type = annotatedType.getType();
sb.insert(0, type.toString());
sb.insert(0, toString(annotatedType.getAnnotations()));
return sb.toString();
}
private static String toString(Annotation[] annotations) {
return Stream.of(annotations).map(Snippet::toSimpleString).
collect(Collectors.joining(" ", "", " "));
}
public static String toSimpleString(Annotation a) {
return "@" + a.annotationType().getSimpleName();
}
}
Markus
More information about the type-annotations-spec-observers
mailing list