Eugene Eugene - 5 months ago 23
Java Question

Raw types and type safety

So, let's say i have a simple list if Integers:

List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
// intList.add("zxc"); // this will obviously fail


Now let's wrap the list inside another list:

List<List<Integer>> listOfListOfInt = new ArrayList<>();
listOfListOfInt.add(intList);


It's all fine. Now let's create the method

private static void method1(List<? extends List> cont) { // [1]
List<? super Date> data = cont.get(0); // [2]
data.add(new Date());
}


Basically i'm adding DATE to the first list in the list, because i'm telling it to treat that list as
List<? super Date>
.
And it works because of the type erasure, of course:

method1(listOfListOfInt);
System.out.println(intList);


prints
[1, 2, Tue Jun 14 23:41:15 BST 2016]
, so my
List<Integer>
now has a Date in it.

Now i understand what's going on, because of the type erasure the
List<? extends List>
becomes just a List, and then it's up to me what to put in it, so i'm putting Date.

Now the question i don't quite understand is that why there is no warning of "Unchecked assignment/calls" at all? According to the java compiler that code is perfectly type safe?

It will give the warning if in the method1 you'll replace
List<? extends List>
to just a
List<List>
for example, or if you'll replace
List<? super Date>
with just a
List
, and so on, generally you'll be warned about unchecked assignments/calls. But in this case you won't, all is fine.

UPDATE: Apparently there's warning by the compiler, but Intellij Idea refuses to show it for some reason. I have "Unchecked warning" inspection set without ignoring anything. And all settings are defaults anyway i think.


IntelliJ IDEA 2016.1.3 Build #IC-145.1617, built on June 3, 2016 JRE:
1.8.0_77-b03 x86 JVM: Java HotSpot(TM) Server VM by Oracle Corporation

Answer

Your issue is the use of a raw type in this declaration: List<? extends List>. The second List is raw. Your issue has nothing to do with type erasure.

When entering the following code in Eclipse, I get warnings.

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        List<Integer> intList = new ArrayList<>();
        intList.add(1);
        intList.add(2);
        List<List<Integer>> listOfListOfInt = new ArrayList<>();
        listOfListOfInt.add(intList);
        method1(listOfListOfInt);
        System.out.println(listOfListOfInt);
    }
    private static void method1(List<? extends List> cont) { // [1]
        List<? super Date> data = cont.get(0);  // [2]
        data.add(new Date());
    }
}

Warnings from Eclipse

[1] List is a raw type. References to generic type List<E> should be parameterized
[2] Type safety: The expression of type capture#1-of ? extends List needs unchecked conversion to conform to List<? super Date>

When compiling with jdk1.8.0_91, I get:

Note: Test.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

Adding -Xlint:unchecked, I get:

Test.java:17: warning: [unchecked] unchecked conversion
                List<? super Date> data = cont.get(0);  // [2]
                                                  ^
  required: List<? super Date>
  found:    CAP#1
  where CAP#1 is a fresh type-variable:
    CAP#1 extends List from capture of ? extends List
1 warning

As you can see, you have been warned, both by the IDE (Eclipse in my case) and by the Java compiler.

Comments