FULL PRODUCT VERSION :
java version "1.8.0_20"
Java(TM) SE Runtime Environment (build 1.8.0_20-b26)
Java HotSpot(TM) 64-Bit Server VM (build 25.20-b23, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
MacOS X 10.9.4
(uname reports: Darwin Kernel Version 13.3.0)
A DESCRIPTION OF THE PROBLEM :
It is not possible to access type annotations on type arguments on an inner class's receiver type. Furthermore, the receiver type, though implicitly the first (synthesized) ctor argument for an inner class, differs in Java reflection from simply examining the first parameter (also available via reflection).
Type annotations via core reflection, in general, seem quite busted in 1.8.0_20. (This is the second bug I've found, and I've also found some omissions and filed two enhancement requests on this same topic.)
I'm guessing this one is closely related to (perhaps same root cause as) this bug: https://bugs.openjdk.java.net/browse/JDK-8039916?page=com.atlassian.jira.plugin.system.issuetabpanels%3acomment-tabpanel
However that bug report says (I think -- I could be misinterpreting it) the fix made it into 1.8.0_20 b20.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the program found below in the "source code" section of this bug report. Observe its output.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Output of the program listed below should include type annotations in the AnnotatedType for the receiver type. Ideally, the same information could also be accessed via the synthesized first constructor argument (since it is the same as the receiver in this case). So, ideally, the program's output would look like so:
param[0] generic #1 => FirstParameterVsReceiverTypeBug<T,U>
param[0] generic #2 => FirstParameterVsReceiverTypeBug<T,U>
param[0] annotated #1 => @FirstParameterVsReceiverTypeBug$A() FirstParameterVsReceiverTypeBug<@FirstParameterVsReceiverTypeBug$B() T,@FirstParameterVsReceiverTypeBug$C() U>
param[0] annotated #2 => @FirstParameterVsReceiverTypeBug$A() FirstParameterVsReceiverTypeBug<@FirstParameterVsReceiverTypeBug$B() T,@FirstParameterVsReceiverTypeBug$C() U>
receiver generic => FirstParameterVsReceiverTypeBug<T,U>
receiver annotated => @FirstParameterVsReceiverTypeBug$A() FirstParameterVsReceiverTypeBug<@FirstParameterVsReceiverTypeBug$B() T,@FirstParameterVsReceiverTypeBug$C() U>
ACTUAL -
Actual output of the program listed below shows that type annotations on type arguments to receiver type cannot be accessed. Furthermore, no annotations or generic type information are available from the first constructor parameter (which, since this is an inner class, is the receiver).
param[0] generic #1 => FirstParameterVsReceiverTypeBug
param[0] generic #2 => FirstParameterVsReceiverTypeBug
param[0] annotated #1 => FirstParameterVsReceiverTypeBug
param[0] annotated #2 => FirstParameterVsReceiverTypeBug
receiver generic => FirstParameterVsReceiverTypeBug
receiver annotated => @FirstParameterVsReceiverTypeBug$A() FirstParameterVsReceiverTypeBug
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedArrayType;
import java.lang.reflect.AnnotatedParameterizedType;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.AnnotatedTypeVariable;
import java.lang.reflect.AnnotatedWildcardType;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
public class FirstParameterVsReceiverTypeBug<T, U> {
@Target(ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
@interface A {
}
@Target(ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
@interface B {
}
@Target(ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
@interface C {
}
class Foo {
Foo(@A FirstParameterVsReceiverTypeBug<@B T, @C U> FirstParameterVsReceiverTypeBug.this) {
}
}
public static void main(String args[]) {
Constructor<?> cons = FirstParameterVsReceiverTypeBug.Foo.class.getDeclaredConstructors()[0];
Type param0GenericType1 = cons.getGenericParameterTypes()[0];
Type param0GenericType2 = cons.getParameters()[0].getParameterizedType();
Type receiverGenericType1 = cons.getAnnotatedReceiverType().getType();
AnnotatedType param0AnnotatedType1 = cons.getAnnotatedParameterTypes()[0];
AnnotatedType param0AnnotatedType2 = cons.getParameters()[0].getAnnotatedType();
AnnotatedType receiverAnnotatedType1 = cons.getAnnotatedReceiverType();
System.out.println("param[0] generic #1 => " + param0GenericType1.getTypeName() + "\n");
System.out.println("param[0] generic #2 => " + param0GenericType2.getTypeName() + "\n");
System.out.println("param[0] annotated #1 => " + toString(param0AnnotatedType1) + "\n");
System.out.println("param[0] annotated #2 => " + toString(param0AnnotatedType2) + "\n");
System.out.println("receiver generic => " + receiverGenericType1.getTypeName() + "\n");
System.out.println("receiver annotated => " + toString(receiverAnnotatedType1) + "\n");
}
// Converts an AnnotatedType to a useful String that includes both type annotations *and* generic
// type information:
private static String toString(AnnotatedType t) {
StringBuilder sb = new StringBuilder();
toString(t, sb);
return sb.toString();
}
private static void toString(AnnotatedType t, StringBuilder sb) {
if (t instanceof AnnotatedParameterizedType) {
for (Annotation a : t.getDeclaredAnnotations()) {
sb.append(a.toString());
sb.append(' ');
}
Type s = t.getType();
assert s instanceof ParameterizedType || s instanceof Class;
if (s instanceof ParameterizedType) {
ParameterizedType p = (ParameterizedType) s;
Type owner = p.getOwnerType();
if (owner != null) {
sb.append(owner.getTypeName());
sb.append('.');
}
sb.append(p.getRawType().getTypeName());
} else {
sb.append(s.getTypeName());
}
sb.append('<');
boolean first = true;
for (AnnotatedType param : ((AnnotatedParameterizedType) t).getAnnotatedActualTypeArguments()) {
if (first) {
first = false;
} else {
sb.append(", ");
}
toString(param, sb);
}
sb.append(">");
} else if (t instanceof AnnotatedArrayType) {
toString(((AnnotatedArrayType) t).getAnnotatedGenericComponentType(), sb);
for (Annotation a : t.getDeclaredAnnotations()) {
sb.append(a.toString());
sb.append(' ');
}
sb.append("[]");
} else if (t instanceof AnnotatedTypeVariable) {
for (Annotation a : t.getDeclaredAnnotations()) {
sb.append(a.toString());
sb.append(' ');
}
TypeVariable<?> tv = (TypeVariable<?>) t.getType();
sb.append(tv.getName());
} else if (t instanceof AnnotatedWildcardType) {
for (Annotation a : t.getDeclaredAnnotations()) {
sb.append(a.toString());
sb.append(' ');
}
AnnotatedWildcardType awt = (AnnotatedWildcardType) t;
AnnotatedType lower[] = awt.getAnnotatedLowerBounds();
AnnotatedType upper[] = awt.getAnnotatedUpperBounds();
AnnotatedType bounds[];
if (lower.length > 0) {
assert upper.length == 1 && upper[0].getType() == Object.class;
bounds = lower;
} else {
bounds = upper;
}
boolean first = true;
for (AnnotatedType bound : bounds) {
if (first) {
first = false;
} else {
sb.append(" & ");
}
toString(bound, sb);
}
} else {
for (Annotation a : t.getDeclaredAnnotations()) {
sb.append(a.toString());
sb.append(' ');
}
sb.append(t.getType().getTypeName());
}
}
}
---------- END SOURCE ----------