Constructor reference - no warning when generics array is created

Vladimir M. :

In Java, it's not possible to create an array of generic type directly:

Test<String>[] t1 = new Test<String>[10]; // Compile-time error

However, we can do this using raw type:

Test<String>[] t2 = new Test[10]; // Compile warning "unchecked"

In Java 8, it's also possible to use a constructor reference:

interface ArrayCreator<T> {
    T create(int n);
}

ArrayCreator<Test<String>[]> ac = Test[]::new; // No warning
Test<String>[] t3 = ac.create(10);

Why doesn't the compiler display the warning in the last case? It still uses raw type to create the array, right?

Holger :

Your question is justified. In short, the method reference does indeed use the raw type (or should use the raw type) and the reason, why the creation of generic arrays is forbidden, still applies when using method references, hence, being able to silently create a function creating a generic array clearly violates the intention of the language design.

The reason why the creation of generic array is forbidden, is that the array type inheritance, stemming from a pre-Generics era, is incompatible with the generic type system. I.e. you can write:

IntFunction<List<String>[]> af = List[]::new; // should generate warning
List<String>[] array = af.apply(10);
Object[] objArray = array;
objArray[0] = Arrays.asList(42);
List<String> list = array[0]; // heap pollution

At this place, it must be emphasized that contrary to some answers here, the compiler does not perform type inference on the expression List[]::new to deduce the generic element type List<String>. It’s easy to prove that generic array creation still is forbidden:

IntFunction<List<String>[]> af = List<String>[]::new; // does not compile

Since List<String>[]::new is illegal, it would be strange if List[]::new was accepted without a warning, by inferring it to be effectively the illegal List<String>[]::new.

JLS §15.13 clearly states:

If a method reference expression has the form ArrayType :: new, then ArrayType must denote a type that is reifiable (§4.7), or a compile-time error occurs.

This already implies that List<String>[]::new is illegal, because List<String> is not reifiable, whereas List<?>[]::new is legal, as List<?> is reifiable, and List[]::new is legal if we consider List to be a raw type, as the raw type List is reifiable.

Then §15.13.1 states:

If the method reference expression has the form ArrayType :: new, a single notional method is considered. The method has a single parameter of type int, returns the ArrayType, and has no throws clause. If n = 1, this is the only potentially applicable method; otherwise, there are no potentially applicable methods.

In other words, the behavior of the List[]::new expression above is the same as if you had written:

    IntFunction<List<String>[]> af = MyClass::create;
…
private static List[] create(int i) {
    return new List[i];
}

except that the method create is only notional. And indeed, with this explicit method declaration, there are only raw type warnings at the create method, but no unchecked warnings regarding the conversion of List[] to List<String>[] at the method reference. So it’s understandable, what happens in the compiler in the List[]::new case, where the method using raw types is only notional, i.e. doesn’t exist in source code.

But the absence of unchecked warnings is a clear violation of JLS §5.1.9, Unchecked Conversion:

Let G name a generic type declaration with n type parameters.

There is an unchecked conversion from the raw class or interface type (§4.8) G to any parameterized type of the form G<T₁,...,Tₙ>.

There is an unchecked conversion from the raw array type G[]ᵏ to any array type of the form G<T₁,...,Tₙ>[]ᵏ. (The notation []ᵏ indicates an array type of k dimensions.)

Use of an unchecked conversion causes a compile-time unchecked warning unless all type arguments Tᵢ (1 ≤ in) are unbounded wildcards (§4.5.1), or the unchecked warning is suppressed by the SuppressWarnings annotation (§9.6.4.5).

So, a conversion of List[] to List<?>[] is legal, as List is parameterized with an unbounded wildcard, but the conversion from List[] to List<String>[] must produce an unchecked warning, which is crucial here, as the use of List[]::new does not produce the raw type warning that appears with an explicit creation method. The absence of raw type warnings seems not to be a violation (as far as I understood §4.8) and it wouldn’t be a problem, if javac created the required unchecked warning.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=425049&siteId=1