views:

84

answers:

2

I would like some variant of this code to compile in java.

class X
{
    List<X> getvalue(){...};
}

class Y extends X
{
    List<Y> getvalue(){...};
}

Javac (1.6) returns an error because List<Y> and List<X> are not compatible.

The point is that I would like the compiler to recognize that List<Y> is a compatible return type to List<X> if Y is a subtype of X. The reason I want this is to simplify the use of a user-defined factory class.

Note: This question is somewhat like this question but for java.

+3  A: 

No, it's not a compatible type. You can't convert from List<Y> to List<X> just because Y is a subclass of X. Consider:

List<Banana> bananas = new ArrayList<Banana>();
List<Fruit> fruit = bananas;
fruit.add(new Apple());
Banana banana = fruit.get(0); // But it's an apple!

You can use bounded wildcards in Java to express limited variance, giving something which is valid:

List<? extends Fruit> fruit = bananas;

because that will prevent you (at compile-time) from trying to add any extra fruit - the compiler knows it might be invalid.

I would recommend Angelika Langer's Java Generics FAQ for further reading about this sort of thing.

Jon Skeet
http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf is recommended for anyone who needs to understand how Java generics work.
Carlos
Thanks, that is a good pointer.
Dennis Heimbigner
The sentence "You can use variance in Java ..." doesn't seem right (if you consider the definition of variance). Maybe you should say "You can use bounded wildcards in Java ..." instead. You could also add that bounded wildcards let you specify the covariance/contravariance constraints on a type-parameter at use-site.
one-zero-zero-one
@one-zero-zero-one: bounded wildcards *give* a form of variance... I'll clarify a bit though.
Jon Skeet
+3  A: 

In Java, the return type of the overriding method has to be covariant with that of the method being overridden.

The class java.util.List is not covariant (In fact, no Java class is. This happens because of the lack of declaration-site variance annotations). In other words, B <: A doesn't imply List<B> <: List<A> (read <: as is-subtype-of). Hence your code doesn't typecheck.


In Java, you have definition-site variance. The following therefore typechecks:

import java.util.List;

class X {
  List<? extends X> getvalue() { return null; }
}

class Y extends X {
  List<Y> getvalue() { return null; }
}
missingfaktor