tags:

views:

114

answers:

4

Lets say that I have the following code:

public class Shelter<A extends Animal, B extends Animal>
{
     List<A> topFloor = new Vector<A>();
     List<B> bottomFloor = new Vector<B>();

     public A getFirstTopFloorAnimal(){return topFloor.get(0);}
     public B getFirstBottomFloorAnimal(){return bottomFloor.get(0);}

     //These 3 methods compile but when I try to use it, they all only return objects
     public List<Animal> getAnimals()
     { 
         List<Animal> a = new Vector<Animal>(topFloor); 
         a.addAll(bottomFloor); 
         return a;
     }

     public List<A> getTopFloor(){return topFloor;}
     public List<B> getBottomFloor(){return bottomFloor;}
}

I then try to do something like the following:

for(Animal a : shelter.getTopFloor()){
    a.growl();
}

But I compiler error that I get the error that A is an object, not an animal. The same happens if I try to use the other methods. Any ideas why this is? Does this have to do with the List<String> is NOT a List<Object> idea in the Generics tutorial?

Thank you

+4  A: 

Generics are only a compile-time concept in order to increase type-safety.

You can't use them to filter the values added to a collection at run-time. You have to do the filtering manually (using instanceOf, for example)

For example this would work and will let you add a Date:

List<String> list = new ArrayList<String>();
((List) list).add(new Date());

So the solution to your (yet-unseen by us) problem is not to use the raw type when adding elements.

Bozho
Thanks for the quick response but, why wouldn't it be known at compile that List<Digraph> + List<DiGraph> = List<DiGraph>?
Sandro
how does this question relate to your original one?
Bozho
Because isn't that what I'm doing in getAnimals()?
Sandro
And it compiles - so the generics part work fine. What do you expect to happen?
Bozho
+1 Edit: ignore my previous comment: i think you're right!
Daniel
Arg, I found the problem within my own code. Thanks for the help!
Sandro
A: 

It's because when you put the items in the Vector, they go in as objects, not instances of Animal.

 public List<Animal> getAnimals()
 { 
     Vector a = new Vector(topFloor); // <-- Not generic!
     a.addAll(bottomFloor); 
     return a;                        // <-- Returning non-generic Vector.
 }

Try this instead:

 Vector<Animal> a = ...
John Feminella
oops, sorry. That is what I had. I edited the question to fix that. Thanks for the catch.
Sandro
should really be List<Animal> a = new Vector<Animal>...
dogbane
Fixed again, thank you.
Sandro
Should *really* be List<Animal> animals = new ArrayList<Animal>();
Daniel
+2  A: 

My best guess is that you've declared shelter to use a raw type as follows:

Shelter shelter = new Shelter();

You really should do the following:

Shelter<Cat,Dog> shelter = new Shelter<Cat,Dog>();

Then you'd actually use the generics features.

See also

  • Effective Java 2nd Edition: Item 23: Don't use raw types in new code.

    If you use raw types, you lose all the safety and expressiveness benefits of generics.

  • JLS 4.8 Raw Types

    The use of raw types is allowed only as a concession to compatibility of legacy code. The use of raw types in code written after the introduction of genericity into the Java programming language is strongly discouraged. It is possible that future versions of the Java programming language will disallow the use of raw types.


List<String> is NOT a List<Object>

Yes, Java generics are are neither covariant nor contravariant. That is only an issue if you're trying to cast one generic type to another, which doesn't seem to be what you're trying to do.

Also, unlike what your subject title says, I don't think this has anything to do with extends to bound the type parameters in generics.

polygenelubricants
+1  A: 

I think the core of your problem is here:

for(Animal a : shelter.getTopFloor()){
    a.growl();
}

How is shelter defined?

Yishai
+1 I didn't see that it was a *compile* error with the loop. It looks like it might be something like Shelter shelter = new Shelter();
Daniel