Wednesday, October 04, 2006

Type-inference weirdness in Java 1.5 with Generics

Surely this has come up for somebody out there before, but I haven't yet found anything about it. It came up today. Essentially: in Java, if Chair is a sort of Thing, a Vector of Chairs is not recognized as a subtype of Vector of Things... this seems wrong.

What I wanted to do was have a method that takes a vector of a certain type (an interface particularly), then pass to it as an argument, a vector of a type that implemented that interface.

So for example:
- Make an interface "Thing" that requires its subclasses to provide "doSomething()"
- ... and a class Chair implements Thing.
- A variable Vector<Chair> chairs
- And a method doEachThing( Vector <Thing> things).
So you should be able to pass chairs as an argument to doEachThing, right?

I get the error:
ThingDoer.java:17: doEachThing(java.util.Vector<Thing>) in ThingDoer cannot be applied to (java.util.Vector<Chair>)

I don't understand this at all. Surely the type-checker should be able to do that inference, that every element in a list of Chairs is in fact a Thing? Am I totally missing the point, or is this a design wrongness about Java?

Anybody else run into something along these lines?

2 comments:

Carl said...

Maybe this will help, from: http://www.25hoursaday.com/weblog/PermaLink.aspx?guid=ec2608d8-3ca6-4dcc-a756-3023859ff94b

Java Generics use "erasure," which drops everything back to Object if you try to say "any type." So when I say <T$gt, it doesn't really mean "anything" like C++/ADA/Python etc. does, it means "Object." Apparently the "correct Java Generic" way of doing this is to define an interface with the speak method in it, and specify that interface as a constraint. this compiles:

interface Speaks { void speak(); }

public class Communicate {
public <T extends Speaks$gt void speak(T speaker) {
speaker.speak();
}
}

Anonymous said...

Read up on wildcards.

Your method should accept a Vector<? extends Thing>, not a Vector<Thing>.

The problem with allowing you to treat a Vector<Chair> as a Vector<Thing> is that you might end up invoking add() on the Vector<Thing> with something besides a chair. Then your Vector<Chair> will contain something that is *not* a Chair.