7

Why does the following code give a compile error?

public MyObject(Builder<? extends MyObject> builder) {
    // Type mismatch: cannot convert from MyObject.Builder<capture#5-of ? extends MyObject> to MyObject.Builder<MyObject>
    Builder<MyObject> myObjBuilder = builder;
}

If the Builder type is a subclass of MyObject, then why can't you assign builder to just type MyObject? I need to do this because I am unable to use an object of type MyObject with the builder. Take a look at this code for example:

public MyObject(Builder<? extends MyObject> builder) {
    // The method getData(capture#8-of ? extends MyObject) in the type Builder<capture#8-of ? extends MyObject> is not applicable for the arguments (MyObject)
    this.data = builder.getData(this);
}

I feel like this should be allowed. Or am I missing something here? Is there a way to do this without casting builder to (Builder<MyObject>) and having to use @SuppressWarnings ?

Also note that I need Builder to be <? extends MyObject> because the MyObject and its Builder will be subclassed (as it is abstract).

Thanks for your help!

User12345
  • 137
  • 2
  • 8
  • @AlexisKing, I don't think this question matches that one, but I do think it needs the same answer as [What are the risks of explicitly casting from a list of type List extends MyObject> to a list of type List in Java?](http://stackoverflow.com/questions/3403293/what-are-the-risks-of-explicitly-casting-from-a-list-of-type-list-extends-myob) – Andrew Aylett Jan 24 '15 at 19:52
  • 2
    See also [Is List a subclass of List? Why aren't Java's generics implicitly polymorphic?](http://stackoverflow.com/q/2745265/465378) – Alexis King Jan 24 '15 at 19:54
  • I guess I wasn't using the right search keywords. I looks for a while. thanks for the link – User12345 Jan 24 '15 at 19:54
  • 2
    Because generics are invariant, read: http://stackoverflow.com/questions/18666710/why-are-arrays-covariant-but-generics-are-invariant – Nir Alfasi Jan 24 '15 at 20:02

1 Answers1

4

Because Foo<? extends Bar> is not a Foo<Bar>.

Say Foo has a method:

void add (T t)

then by contract, you can only add T objects. Now if T is instantiated as ? extends Bar, we don't know the type. Accepting Bar could lead to problematic behavior: if you have an ArrayList<Foo> you expect the ArrayList to contain only Foo instances. If you would see the ArrayList<Foo> as a ArrayList<SuperFoo>, one can't guarantee that the ArrayList<Foo> contains only Foo's.

Some languages enable covariance: if you only use T as output, you can say that a class Foo<T> is a the same as Foo<SuperT> as well (the same with input). But currently Java doesn't support that. C# however does on the interface level.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • But a method `add(Object)` actually *does* accept any subclass of `Object`, isn't it? – MC Emperor Jan 24 '15 at 20:04
  • 1
    @MCEmperor: a subclass yes. But here you want to "superclass" it. Thus you want to convert a `ArrayList` to `ArrayList`. That would imply that `add(Integer)` would accept an `Object`. Evidently that's not possible. – Willem Van Onsem Jan 24 '15 at 20:05
  • I read up on covariance and invariance, and this all makes sense to me now. With my case however, I know that a cast to Builder of type MyObject will be type safe at runtime, so I guess It will be ok to do so even though it is ugly. Thanks everyone! – User12345 Jan 24 '15 at 20:17
  • @User12345: sure, you can do an explicit type cast, but if you add items at runtime to the `ArrayList`, you probably will get an error, so only use it when it is absolutely necessary. – Willem Van Onsem Jan 24 '15 at 20:22