I don’t think I really understand Java generics. What’s the difference between these two methods? And why does the second not compile, with the error shown below.
Thanks
static List<Integer> add2 (List<Integer> lst) throws Exception {
List<Integer> res = lst.getClass().newInstance();
for (Integer i : lst) res.add(i + 2);
return res;
}
.
static <T extends List<Integer>> T add2 (T lst) throws Exception {
T res = lst.getClass().newInstance();
for (Integer i : lst) res.add(i + 2);
return res;
}
Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - incompatible types
required: T
found: capture#1 of ? extends java.util.List
For the second method to compile, you have to cast the result of
newInstace()toT:Regarding the difference between the two methods, let’s forget about the implementation, and consider only the signature.
After the code is compiled, both methods will have exactly the same signature (so the compiler would give an error if the have the same name). This happens because of what is called type erasure.
In Java, all the type parameters disappear after compilation. They are replaced by the most generic possible raw type. In this case, both methods will be compiled as
List add2(List).Now, this will show the difference between the two methods:
The line marked as
// ERROR!won’t compile.In the first method,
add1, the compiler knows that it can assign the result to a variable of typeArrayList<Integer>, because the signature states that the return type of the method is exactly the same as that of the parameter. Since the parameter is of typeArrayList<Integer>, the compiler will inferTto beArrayList<Integer>, which will allow you to assign the result to anArrayList<Integer>.In the second method, all the compiler knows is that it will return an instance of
List<Integer>. It cannot be sure that it will be anArrayList<Integer>, so you have to make an explicit cast,ArrayList<Integer> l2 = (ArrayList<Integer>) add2(l);. Note that this won’t solve the problem: you are simply telling the compiler to stop whining and compile the code. You will still get an warning (unchecked cast), which can be silenced by annotating the method with@SuppressWarnings("unchecked"). Now the compiler will be quiet, but you might still get aClassCastExceptionat runtime!