Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is List<Number> not a sub-type of List<Object>?

public void wahey(List<Object> list) {}

wahey(new LinkedList<Number>());

The call to the method will not type-check. I can't even cast the parameter as follows:

wahey((List<Object>) new LinkedList<Number>());

From my research, I have gathered that the reason for not allowing this is type-safety. If we were allowed to do the above, then we could have the following:

List<Double> ld;
wahey(ld);

Inside the method wahey, we could add some Strings to the input list (as the parameter maintains a List<Object> reference). Now, after the method call, ld refers to a list with a type List<Double>, but the actual list contains some String objects!

This seems different to the normal way Java works without generics. For instance:

Object o;
Double d;
String s;

o = s;
d = (Double) o;

What we are doing here is essentially the same thing, except this will pass compile-time checks and only fail at run-time. The version with Lists won't compile.

This leads me to believe this is purely a design decision with regards to the type restrictions on generics. I was hoping to get some comments on this decision?

like image 377
ares Avatar asked Aug 06 '09 17:08

ares


3 Answers

What you are doing in the "without generics" example is a cast, which makes it clear that you are doing something type-unsafe. The equivalent with generics would be:

Object o;
List<Double> d;
String s;

o = s;
d.add((Double) o);

Which behaves the same way (compiles, but fails at runtime). The reason for not allowing the behavior you're asking about is because it would allow implicit type-unsafe actions, which are much harder to notice in code. For example:

public void Foo(List<Object> list, Object obj) {
  list.add(obj);
}

This looks perfectly fine and type-safe until you call it like this:

List<Double> list_d;
String s;

Foo(list_d, s);

Which also looks type-safe, because you as the caller don't necessarily know what Foo is going to do with its parameters.

So in that case you have two seemingly type-safe bits of code, which together end up being type-unsafe. That's bad, because it's hidden and therefore hard to avoid and harder to debug.

like image 181
Tyler McHenry Avatar answered Sep 18 '22 20:09

Tyler McHenry


Consider if it was...

List<Integer> nums = new ArrayList<Integer>();
List<Object> objs = nums
objs.add("Oh no!");
int x = nums.get(0); //throws ClassCastException

You would be able to add anything of the parent type to the list, which may not be what it was formerly declared as, which as the above example demonstrates, causes all sorts of problems. Thus, it is not allowed.

like image 29
James Avatar answered Sep 21 '22 20:09

James


They aren't subtypes of each other due how generics work. What you want is to declare your function like this:

public void wahey(List<?> list) {}

Then it will accept a List of anything that extends Object. You can also do:

public void wahey(List<? extends Number> list) {}

This will let you take in Lists of something that's a subclass of Number.

I'd recommend you pick up a copy of "Java Generics and Collections" by Maurice Naftalin & Philip Wadler.

like image 25
Norman Avatar answered Sep 19 '22 20:09

Norman