Win a copy of Design for the Mind this week in the Design forum!
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic

upper bound wildcard is readonly but lower bound are not

 
Kartik Patel
Ranch Hand
Posts: 73
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Why upper bound wildcard generics make the collection readonly but same not apply for lower bound wildcard collection?

List<? extends Number> l= new ArrayList<Integer>();
Number n1= new Double(1.0);
l.add(n1) // won't compile

List<? super Double> l= new ArrayList<Number>();
Number n1= new Double(1.0);
Double n2= new Double(1.0);
l.add(n1) // won't compile
l.add(n2) // will compile
 
Rob Spoor
Sheriff
Pie
Posts: 20511
54
Chrome Eclipse IDE Java Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
With List<? extends Number>, you can't add anything because you don't know which Number subclass you can actually add. Is this list in fact List<Integer>, List<Double>, List<Float>? You don't know! Therefore, you can't add a Double - it might break the rules for the list.

With List<? super Double>, you know that the generic type is a Double or any of its super classes - Number or Object. So let's consider the options:
List<Double> - no problem in adding a Double here
List<Number> - a Double is a Number, so no problem here either
List<Object> - anything is an object, so no problem here either


When retrieving it's the other way around. With List<? extends Number>, you don't know what the type is, except that it is Number or a sub class. Therefore, you can safely assign any element to a Number reference. With List<? super Double>, you don't know what type it actually is; you have three options. You can't assume it's a Double or a Number, so you can only assign it to Object references without explicit casting.
 
Kartik Patel
Ranch Hand
Posts: 73
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Rob Prime:


With List<? super Double>, you know that the generic type is a Double or any of its super classes - Number or Object. So let's consider the options:
List<Double> - no problem in adding a Double here
List<Number> - a Double is a Number, so no problem here either
List<Object> - anything is an object, so no problem here either


I think adding List<Number> and List<Object> won't compile in case of List<? super Double>. Only List<Double> will compile. Please try.
 
Rob Spoor
Sheriff
Pie
Posts: 20511
54
Chrome Eclipse IDE Java Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I was not talking about adding the lists, but adding a Double to any of these types. In code:

Compiles without a problem.
 
Kartik Patel
Ranch Hand
Posts: 73
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I am talking about this. See following tree
Vehicle
Honda Tata
City Civic Indigo Indica

Check this:

class Vehicle {}
class Honda extends Vehicle {}
class Tata extends Vehicle {}
class City extends Honda {}
class Civic extends Honda {}
class Indica extends Tata {}
class Indigo extends Tata {}

List<? super Honda> ls=null;

Honda honda=new Honda();
Civic civic=new Civic();
Vehicle vehicle=new Vehicle();


ls.add(honda); // Fine Honda super Honda
ls.add(civic); // ITS WORKING WHY??? civic is not super of Honda
ls.add(vehicle); // Its not WORKING WHY??? Vehicle super Honda right?


Please suggest..
 
Rob Spoor
Sheriff
Pie
Posts: 20511
54
Chrome Eclipse IDE Java Windows
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Kartik Patel:
ls.add(honda); // Fine Honda super Honda
ls.add(civic); // ITS WORKING WHY??? civic is not super of Honda
ls.add(vehicle); // Its not WORKING WHY??? Vehicle super Honda right?

A Civic object will pass the "instanceof Honda" test so that is why you can add elements. You can't prevent this using generics - if a method can accept a Honda object, it can accept a Civic object too. The only thing you could do is checking exact class matching (if (civic.getClass() == Honda.class)).

Now why you cannot add a Vehicle object:
List<? super Honda> means that it could, in fact, be a List<Honda>. It could be a List<Vehicle> as well, but you don't know that. As far as adding goes, it IS a List<Honda>.

If you want to be able to add Vehicles, you need to declare the list to allow Vehicles:
- List<Vehicle>
- List<? super Vehicle>
- List<Object> // equals List<? super Object>

That will still allow Civics and Hondas though.


The super does not means that it accepts only super classes of Honda. It means that the type is Honda or a super class. And as I said before, anywhere you can use a Honda, you can use a Civic.

Similarly, List<? extends Honda> means that the type is Honda or a sub class. Therefore, you know that everything that is stored in it is at least a Honda; it might be a Civic, or an Indigo, you don't know that without runtime checking, but it is still a Honda. But since you don't know that type, you can't add any Honda object to it. Imagine you want to add a Civic but it is in fact a List<Indigo>. So much for your type safe List...


So the rule is as such, for any class X:
List<? super X> can add objects of X or a sub class. You can only retrieve elements as Object though; everything else needs to be cast.
List<? extends X> cannot have anything added at all. You can retrieve elements as X or any super class of X.
List<X> can add objects of X or a sub class. You can retrieve elements as X or any super class of X.


If you don't understand, please (re)read the Generics Trail of the Sun Java tutorial.
 
Kartik Patel
Ranch Hand
Posts: 73
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks Rob. The explanation was quite informative.
 
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic