Consider the following code sample:
import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { List list = new ArrayList<Integer>(); String response = getProducer(list).get(); } static Producer<String> getProducer(List<Integer> list) { return new Producer<String>(); } } class Producer<T> { T get() { return null; } }
When compiled in Java 7 it just produces an expected warning for getProducer(list)
:
Warning:(7, 39) java: unchecked conversion required:
java.util.List<java.lang.Integer>
found:java.util.List
However, when compiled in Java 8 it produces the following error for the response = getProducer(list).get()
assignment:
Error:(7, 48) java: incompatible types:
java.lang.Object
cannot be converted tojava.lang.String
So apparently the type returned from getProducer(list)
is not Producer<String>
, but erased Producer
(which is also confirmed by means of the 'extract variable' feature in the IDE). This is very puzzling because getProducer
method always returns Producer<String>
.
Oddly enough it could be fixed by avoiding unchecked conversion while calling getProducer
method, either by:
getProducer
from List<Integer>
to List
list
variable from List
to List<Integer>
The warning message “unchecked conversion” implies that we should check the conversion before the assignment. To check the type conversion, we can go through the raw type collection and cast every element to our parameterized type.
An unchecked warning tells a programmer that a cast may cause a program to throw an exception somewhere else. Suppressing the warning with @SuppressWarnings("unchecked") tells the compiler that the programmer believes the code to be safe and won't cause unexpected exceptions.
(Yes, this is legal code; see Java Generics: Generic type defined as return type only.) The return type will be inferred from the caller.
Type inference is a Java compiler's ability to look at each method invocation and corresponding declaration to determine the type argument (or arguments) that make the invocation applicable.
This looks like a known compatibility issue reported here and here.
From the second link:
The following code which compiled, with warnings, in JDK 7 will not compile in JDK 8:
import java.util.List; class SampleClass { static class Baz<T> { public static List<Baz<Object>> sampleMethod(Baz<Object> param) { return null; } } private static void bar(Baz arg) { Baz element = Baz.sampleMethod(arg).get(0); } }
Compiling this code in JDK 8 produces the following error:
SampleClass.java:12: error:incompatible types: Object cannot be converted to Baz
Baz element = Baz.sampleMethod(arg).get(0);
Note: SampleClass.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. 1 error
Deriving from this, the OP's code can be fixed by replacing this line (the type declartion on the right hand side threw me off - I read it as a typed array list which it is not):
List list = new ArrayList<Integer>();
with
List<Integer> list = new ArrayList<Integer>();
which will not result in type being being erased from return type of method getProducer(List<Integer> list)
Quote from second link again:
In this example, a raw type is being passed to the
sampleMethod(Baz<Object>)
method which is applicable by subtyping (see the JLS, Java SE 7 Edition, section 15.12.2.2). An unchecked conversion is necessary for the method to be applicable, so its return type is erased (see the JLS, Java SE 7 Edition, section 15.12.2.6). In this case the return type ofsampleMethod(Baz<Object>)
isjava.util.List
instead ofjava.util.List<Baz<Object>>
and thus the return type ofget(int)
isObject
, which is not assignment-compatible withBaz
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With