2

Suppose I have the following interface:

public interface Interface<T extends Number>{
    public Vector<Interface<T>> getVector();
}

and the following class implementing that interface:

public abstract class C<T extends Number> implements Interface<T>{

    private Vector<C<T>> vector;
    public Vector<Interface<T>> getVector(){ //errror
         return this.vector;
    }
} 

Why is not legal returning a Vector<C<T>> meanwhile is legal ( obviously) returning a Vector<Interface<T>>. C is actually implementing Interface, so it should be possible, right? What am I missing?

EDIT:

why this work for non generics interface? Is this actually a generic related problem?

public interface Interface{
        public Interface getVector();
    }

public abstract class C implements Interface {

    private C;
    public Interface getVector(){ //errror
         return this.c;
    }
} 
Heisenbug
  • 38,762
  • 28
  • 132
  • 190
  • The technical term for what you are describing is **covariance**. Search for that, and you'll find lots of duplicates, including this one: [Any simple way to explain why I cannot do `List animals = new ArrayList()`?](http://stackoverflow.com/questions/2346763/any-simple-way-to-explain-why-i-cannot-do-listanimal-animals-new-arraylistdo) – Daniel Pryden Sep 02 '11 at 04:44
  • @Daniel Pryden: thank you for the technical term. – Heisenbug Sep 02 '11 at 07:39

2 Answers2

2

Because the Vector is explicitly made up of Interface<T>, not things that extend Interface<T>, I believe this would work if you changed the definition to

 public Vector<? extends Interface<T>> getVector();

The problem is that for some V implements T or V extends T that Foo<V> is not a supertype of Foo<T>. The compiler does not test inheritance on the generic arguments unless you explicitly indicate that extension point.

Using Vector<? extends Interface<T>> means "allow any class that implements or extends Interface<T>, whereas Vector<Interface<T>> means a vector consisting only of Interface<T> items.

Perhaps it's more concrete to consider that List<Integer> is not an acceptable replacement for List<Number> despite Integer extending Number for precisely the same reason.

update:

I tested this and the following compiles without any errors or warnings

interface Interface<T extends Number>{
    public Vector<? extends Interface<T>> getVector();
}

abstract class C<T extends Number> implements Interface<T>{

    private Vector<C<T>> vector;
    public Vector<? extends Interface<T>> getVector(){
         return this.vector;
    }
} 
Mark Elliot
  • 75,278
  • 22
  • 140
  • 160
  • You also have to change the method signature to `public Vector> getVector()` in subclass `C` for this to compile. `Vector>` is not a subclass of `Vector>`, so returning something that is not a subclass of what the method is supposed to return is obviously not allowed. – toto2 Sep 01 '11 at 23:59
  • +1: but I edited my question. Could you help me to clarify that? is a problem related to generics right? – Heisenbug Sep 02 '11 at 00:03
  • @toto2: no, it compiles just fine with the modification I suggest (see my edit) – Mark Elliot Sep 02 '11 at 00:04
  • OK. I had left `Vector>` in the method declaration for `C`. Nonetheless, I think it's clearer to use `Vector>` as the return type in the declaration. – toto2 Sep 02 '11 at 00:08
1

It's the way generics work. They are not "covariant": if you have class AClass and its subclass SubAClass, Vector<SubAClass> is not a subclass of Vector<A>. (Note however that SubAClass[] is a subclass of AClass[].)

EDIT:

The seemingly obvious:

public ArrayList<Object> blah() {
    return new ArrayList<String>();
}

won't compile because ArrayList<String> is not a subclass ArrayList<Object>. So in your case you can't return a Vector<C<T>> instance for a Vector<Interface<T>>.

toto2
  • 5,306
  • 21
  • 24
  • +1: so my guess is correct right? Is a problem related to generics? – Heisenbug Sep 02 '11 at 00:04
  • 2
    @toto2: Reification has nothing to do with it. Your examples are correct, but the reason it works the way it does is not because of erasure, but because Java's generics are *invariant* (and the `T extends Foo` or `T super Foo` syntaxes are just a workaround for this) but the feature desired here is *covariance*. True, arrays are reified, and arrays are covariant, but the two are completely independent qualities. – Daniel Pryden Sep 02 '11 at 04:29
  • Thanks @Daniel. I took out my Effective Java by Bloch where covariant and reified are defined at the start of Item 25. I indeed inverted their meaning. I edited my answer. For reference, arrays being reified means that when you add a String to an array `myArray[0] = "blah"` there is a runtime check that `myArray` is indeed a `String[]` (or a supertype of `String[]`). An `ArrayStoreException` is thrown if it's not the case. In some way, reification is the opposite model from type erasure used by generics. – toto2 Sep 02 '11 at 13:48