aspose file tools*
The moose likes Java in General and the fly likes Generics: Shape example does not work as SUN says Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » Java in General
Bookmark "Generics: Shape example does not work as SUN says" Watch "Generics: Shape example does not work as SUN says" New topic
Author

Generics: Shape example does not work as SUN says

Mohamed Farouk
Ranch Hand

Joined: Jun 08, 2005
Posts: 249
Hello Friends

http://java.sun.com/docs/books/tutorial/extra/generics/wildcards.html

In the example provided Sun says:


Now, the type rules say that drawAll() can only be called on lists of exactly Shape: it cannot, for instance, be called on a List<Circle>. That is unfortunate, since all the method does is read shapes from the list, so it could just as well be called on a List<Circle>. What we really want is for the method to accept a list of any kind of shape:



But I tried to see what happens when the method is like this:

public void drawAll(List<Shape> shapes) {
...
}
Actually it compliles and runs fine any idea why?

Please can you check and come back to me which is not right?


See attached file:



Would appreciate if you can post your thaughts on this?

SCJP, SCWCD, SCBCD, SCEA 5
David Newton
Author
Rancher

Joined: Sep 29, 2008
Posts: 12617

It won't compile for me, gives a warning saying it can't be applied.

How are you compiling this?
Mohamed Farouk
Ranch Hand

Joined: Jun 08, 2005
Posts: 249
Hello

I am using spring source ide and i tried again at home just copy/paste the code and run it again


This is the output.
Not sure why this is problem for you
David Newton
Author
Rancher

Joined: Sep 29, 2008
Posts: 12617


You're passing in a list of Shape, of course *that* will run. That's what the tutorial says as well.

The tutorial says that passing a List<Circle> (or any other Shape subclass) *won't* compile, which is correct.

I'm not sure what your question is, I guess; sorry.
John de Michele
Rancher

Joined: Mar 09, 2009
Posts: 600
David,

I think he's confusing passing a List<Circle>, with passing a List<Shape> that has some Circle (or other Shape sub-class) members in it.

Mohamed,

A List<Circle> is not a subclass of List<Shape>, even though Circle is a subclass of Shape. Generics don't work that way.

John.
Mohamed Farouk
Ranch Hand

Joined: Jun 08, 2005
Posts: 249
Thank both for both your quick replies

John

Yes your right I am constructing List<Shape> list = new ArrayList<Shape>(); But if List<Shape> is different from List<Circle> how am i able to add to the ArrayList<Shape> a object of type List<Circle>.


Yes again if I pass List<Circle> = new ArrayList<Circle> the compilation fails on drawAll method. But If I change the drawAll to accept a bounded Shape then works?

Please can you guys shed some light on this behaviour thanks!!!


David Newton
Author
Rancher

Joined: Sep 29, 2008
Posts: 12617

Mohamed Farouk wrote:Yes your right I am constructing List<Shape> list = new ArrayList<Shape>(); But if List<Shape> is different from List<Circle> how am i able to add to the ArrayList<Shape> a object of type List<Circle>.

Firstly, you're not adding an ArrayList<Shape> to the List<Circle>, you're adding a Circle. And that works because a Circle is-a Shape.

Yes again if I pass List<Circle> = new ArrayList<Circle> the compilation fails on drawAll method. But If I change the drawAll to accept a bounded Shape then works?

What do you mean by a "bounded shape"?

As far as I can tell the tutorial is correct. You can pass a List<Shape> to drawAll(List<Shape>). What's *in* that List<Shape> is anything that extends Shape.

You cannot, however, pass a List<Circle> to drawAll(List<Shape>), just like the tutorial says: a List<Circle> is-NOT-a List<Shape>. But you *can* pass it to drawAll(List<? extends Shape>), also as the tutorial states.
John de Michele
Rancher

Joined: Mar 09, 2009
Posts: 600
Mohamed:

Have you actually tried adding an ArrayList<Shape> to a List<Circle>? I got an error when I tried. This also fails:

I think you meant adding an object of type Circle to an ArrayList<Shape>? That is possible. Since Circle is a subclass of Shape, Circle is a Shape.

John.
Mohamed Farouk
Ranch Hand

Joined: Jun 08, 2005
Posts: 249
Guys thanks but
Why is this wrong then
List<Object> ls = new ArrayList<Object>(); // 1
List<String> lo = ls; // 2


An String is a subtype of Object so why cant the assignment work when Like you said List<Circle> can be added to List<Shape> as circle is a shape?

I get compilation error on both assigments
List<Object> ls = new ArrayList<Object>(); // 1
List<String> lo = ls; // 2

and
List<String> ls = new ArrayList<String>(); // 1
List<Object> lo = ls; // 2

Basically it would be better to have clear clarity on the following circumstances.
1. A <T> can be added to another Generic of type List<X> (like if T is a subtype of X)
2. List<T> can be assigned to another Generic of type List <X>
3. <T> can be retrieved from another type <X> (in a for loop)


David Newton
Author
Rancher

Joined: Sep 29, 2008
Posts: 12617

Like you said List<Circle> can be added to List<Shape> as circle is a shape?

Please stop saying "a List<Circle> can be added to a List<Shape>". That is incorrect. A Circle can be added to a List<Shape>, because a Circle is-a Shape.

But that's not what the code above is doing: there you're trying to *assign* a List<Object> to a List<String>, which as the tutorial states, will not work.How about:Now does it make sense?
Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19760
    
  20

Mohamed Farouk wrote:List<Object> ls = new ArrayList<Object>(); // 1
List<String> lo = ls; // 2

and
List<String> ls = new ArrayList<String>(); // 1
List<Object> lo = ls; // 2

It's a good thing both do not compile.

If they would compile, the first would allow this:
The second would allow this:
Using List<? extends Object> solves this issue by simply not allowing adding anything except null.


SCJP 1.4 - SCJP 6 - SCWCD 5 - OCEEJBD 6
How To Ask Questions How To Answer Questions
Istvan Kovacs
Ranch Hand

Joined: May 06, 2010
Posts: 100
Yes again if I pass List<Circle> = new ArrayList<Circle> the compilation fails on drawAll method. But If I change the drawAll to accept a bounded Shape then works?


If you modify drawAll as:

You're writing that 'drawAll takes as parameter a List object; the List is parameterised with a concrete but unknown subtype of Shape'.
If you invoke this with List<Circle> or List<Rectangle> or List<Shape>, they will all work.
When you retrieve objects from such a List, they'll be of type Shape. You cannot add objects of any type to such a List, to avoid violating the contract that it contains a specific (although unknown) subtype of Shape; say it was a List<Circle>, and you'd be able to add Rectangle objects if add() was allowed on such a List. Note that add(aRectangle) is legal is the List is List<Shape>, as then the guarantee is simply that it holds Shapes.

You can play a trick with such unknown but concrete subtypes. Say you have a generic interface implemented by some classes (I used Foo<S extends Shape>, but this could be List<S extends Shape>), and you want to write a class that can work with any of those implementations.


This way, type safety is guaranteed within doUseFoo.
John de Michele
Rancher

Joined: Mar 09, 2009
Posts: 600
Mohamed:

The reason it doesn't work is because generics are not transitive. In other words, Circle is a subclass of Shape, but List<Circle> is not a subclass of List<Shape>.

John.
 
jQuery in Action, 2nd edition
 
subject: Generics: Shape example does not work as SUN says