I would like to understand better what happens when the Java compiler encounters a call to a method like the one below.
<T extends AutoCloseable & Cloneable>
void printType(T... args) {
System.out.println(args.getClass().getComponentType().getSimpleName());
}
// printType() prints "AutoCloseable"
It is clear to me that there is no type <T extends AutoCloseable & Cloneable>
at runtime, so the compiler makes the least wrong thing it can do and creates an array with the type of one of the two bounding interfaces, discarding the other one.
Anyway, if the order of the interfaces is switched, the result is still the same.
<T extends Cloneable & AutoCloseable>
void printType(T... args) {
System.out.println(args.getClass().getComponentType().getSimpleName());
}
// printType() prints "AutoCloseable"
This led me to do some more investigation and see what happens when the interfaces change. It seems to me that the compiler uses some kind of strict order rule to decide which interface is the most important, and the order the interfaces appear in code plays no role.
<T extends AutoCloseable & Runnable> // "AutoCloseable"
<T extends Runnable & AutoCloseable> // "AutoCloseable"
<T extends AutoCloseable & Serializable> // "Serializable"
<T extends Serializable & AutoCloseable> // "Serializable"
<T extends SafeVarargs & Serializable> // "SafeVarargs"
<T extends Serializable & SafeVarargs> // "SafeVarargs"
<T extends Channel & SafeVarargs> // "Channel"
<T extends SafeVarargs & Channel> // "Channel"
<T extends AutoCloseable & Channel & Cloneable & SafeVarargs> // "Channel"
Question: How does the Java compiler determine the component type of a varargs array of a parameterized type when there are multiple bounds?
I'm not even sure if the JLS says anything about this, and none of the information I found by googling covers this particular topic.
T[]
, whereT
is the actual type inferred for the invocation ofprintType()
. This would normally be straightforward, since the erasure of a type variable is its left-most bound, but I think that the bounds are being reordered inadvertently at some point by the processes described in Chapter 18 (type inference). – HatcherT
delimited by&
in theTypeBound
, the boundαl <: T[P1:=α1, ..., Pp:=αp]
appears in the set". The phrase "appears in the set" doesn't seem to specify any sort of indexing scheme. I think that personally I would have to review the entire chapter to feel comfortable being sure about this, though. – Hatcherleftmost
while some other saysappears in the set
(and you are right about no indexes what-so-ever) – Long(Runnable[])arr
doesn’t throw in your example while(Runnable[])(Object[])arr
does, is that the compiler removes the cast in the former expression, as the formal type already isRunnable[]
. Casting to a wider type first makes the narrowing cast necessary in the compiled code. As my answer shows, you can get even worse things than anArrayStoreException
(and I’m currently investigating whether there are more things to exploit)… – Consolidation