4

I have the following classes:

    public interface ModelObject {
    }
    public interface Resource {
    }
    public interface Transformer <F,T>{
    }
    public interface WrapperFactory {
        Transformer<Resource, Wrap<? extends ModelObject>> createMapper();
    }
    public class Wrap<E extends ModelObject> {

    }

    public class AbstractBaseTransformer<F,T> implements Transformer<F,T> {
    }
    public class ConcreteModel implements ModelObject {

    }
    public class ConcreteTransformer extends AbstractBaseTransformer<Resource, Wrap<ConcreteModel>> {

    }
    public class ConcreteFactory implements WrapperFactory {

        @Override
        public Transformer<Resource, Wrap<? extends ModelObject>> createMapper() {
            return new ConcreteTransformer();
        }
    }

The ConcreteFactory doesn't compile stating that ConcreteTransformer is incompatible with returned

Transformer<Resource, Wrap<? extends ModelObject>>

I can't see what's wrong here. ConcreteTransformer binds 1st parameter to Resource (same as expected) while binding 2nd parameter to:

Wrap<ConcreteModel>

which should bind to:

Wrap<? extends ModelObject> 

as ConcreteModel implements it.

user207421
  • 305,947
  • 44
  • 307
  • 483
dsmog
  • 71
  • 4
  • Is this happening in Eclipse or in the JDK? The Eclipse compiler contains a few bugs. – gparyani Aug 16 '13 at 22:13
  • 2
    @dsmog The correct terminology is "generics" (Java), "template classes" (A syntactically similar but conceptually very different concept in C++). – Jason C Aug 16 '13 at 22:24

3 Answers3

4

Here is a simpler version, to narrow down the issue:

interface ModelObject {}
class ConcreteModel implements ModelObject {}

class Wrap<E extends ModelObject> {}
class SomeGeneric<T> {}

class Simple {
    public SomeGeneric<Wrap<? extends ModelObject>> m() {
        return new SomeGeneric<Wrap<ConcreteModel>>();
    }
}

does not compile either.

Your problem is that a SomeGeneric<Wrap<ConcreteModel>> is not a SomeGeneric<Wrap<? extends ModelObject>>.

Community
  • 1
  • 1
assylias
  • 321,522
  • 82
  • 660
  • 783
2

Wrap<ConcreteModel> is a subtype of Wrap<? extends ModelObject>? Yes.

Transformer<Resource, Wrap<ConcreteModel>> is a subtype of Transformer<Resource, Wrap<? extends ModelObject>>? No.

It's the same as:

String is a subtype of Object? Yes.

List<String> is a subtype of List<Object>? No.

Basically, for parameterized types to be compatible, if the top-level parameter is not wildcard, then the parameters must match exactly. In your case, the top-level parameter is not wildcard, and the parameters don't match exactly.

What you probably wanted instead was

Transformer<Resource, ? extends Wrap<? extends ModelObject>>

newacct
  • 119,665
  • 29
  • 163
  • 224
0

A Wrap<ConcreteModel> can be assigned to a variable of type Wrap<? extends ModelObject>. But the matter here is more complex.

Assume you have a ArrayList<Wrap<? extends ModelObject>> list. When you have such a type, it means that you can add a Wrap<ConcreteModel> to the list, but it also means that you can add a Wrap<ModelObject> to it. In brief, it means you have a list, that can contain a Wrap of anything that can be cast to a ModelObject.

On the other side, having a ArrayList<Wrap<ConcreteModel>> list means you can only add Wrap<ConcreteModel>s to it, while a Wrap<ModelObject> cannot be added to it, because the list can only contain wrapped ConcreteModels, and a wrapped ModelObject is not a wrapped ConcreteModel nor it can be cast to be one.

This is exactly your case. You declared your createMapper() method to return a Transformer<Resource, Wrap<? extends ModelObject>>. This means that the second argument of the returned Transformer must be able to be any subclass of ModelObject, including ModelObject itself. On the contrary, you are trying to return a Transformer<Resource, Wrap<ConcreteModel>>.

The compiler needs to enforce this because Transformer<F, T> could declare a method:

void myMethod(F fObject, T tObject);

If that was the case, the method myMethod of an object of type Transformer<Resource, Wrap<? extends ModelObject>> would accept an object of type ModelObject as its second argument. On the other side, the same method, in a object of type Transformer<Resource, Wrap<ConcreteModel>> cannot accept a ModelObject as its second argument.

Giulio Franco
  • 3,170
  • 15
  • 18