3

I'm trying to understand java generic covariancy, and I understand why

List<Number> list = new ArrayList<Integer>();
Integer = list.get(0);

is disallowed, so that a float isn't put into list and later cause issues. I believe type erasure would compile this down to:

List list = new ArrayList();
Integer = (Integer) list.get(0);

This is allowed:

List<? extends Number> list = new ArrayList<Integer>();
Integer = list.get(0);

Doesn't this compile down to the same thing after type erasure? How does the wildcard help the compiler in enforcing that a float isn't put into the list when it holds Integers?

Siddhartha
  • 4,296
  • 6
  • 44
  • 65

2 Answers2

5

If you have List<? extends Number> list, you just cannot put anything into the resulting list. Your list is the "list containing some objects which extend Number", so calling add() with any argument is not compatible with this type. So this is how compiler controls whether you don't put floats: it does not allow to put anything.

Usually you change type to List<? extends Number> only at the places where you are not going to modify the list anymore and want just to read it.

Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
1

Very well answered by Tagir and I would like to add few more points. First of all the following will not even compile:

List<? extends Number> list = new List<Integer>();

I hope you know List is an interface. So now we have:

List<? extends Number> list = new ArrayList<Integer>();

Here list is a reference of type List<? extends Number> and it simply means it can refer to a list in which every element IS Number or more specifically every element extends class Number so it means all the following are permissible:

list = new ArrayList<Float>();
list = new ArrayList<Long>();

But we can not instantiate as a list of Object as Object is not Number.

list = new ArrayList<Object>(); // compile time error

Now when we take element out of this list we know only one thing for sure that it is going to be a Number and it means not sure but it can be Integeror Long as well. So we need casting as:

Integer integer = (Integer) list.get(0);
Float floatValue = (Float) list.get(0);
Number number = list.get(0); // no casting if assigning it to Number

This is mainly useful when we are producing the elements and not consuming them (check PECS). For example useful in printing the elements but not useful when you want to add any element in it as it will not even compile.

private static void printMe(List<? extends Number> numbers) {
        for(Number number : numbers) {
            System.out.println(number);
        }
}

This can be invoked then as:

List<Integer> integerList = new ArrayList<>();
integerList.add(10);integerList.add(20);integerList.add(30);
printMe(integerList);

List<Long> longList = new ArrayList<>();
longList.add((long) 4.5);longList.add((long) 6.5);longList.add((long) 7.5);
printMe(longList);

This will simply print the elements in each list.

Community
  • 1
  • 1
akhil_mittal
  • 23,309
  • 7
  • 96
  • 95