Question

I am struggling to make this work:

public abstract class MapperFactory<M extends TaskMapper<? extends Message, ? extends Message, ? extends TaskForm>> {

    public static <M extends TaskMapper<? extends Message, ? extends Message, ? extends TaskForm>> MapperFactory<M> getMapperFactory(Message msgIn, Message msgOut) {

        if (msgIn.isMyMapper())
            return new MyTaskMapperFactory();

        throw new IllegalStateException("Mapper not found!");
    }

    public abstract TaskMapper<? extends Message, ? extends Message, ? extends TaskForm> getTaskMapper();

    public static class MyTaskMapperFactory extends MapperFactory<MyTaskMapper> {

        @Override
        public TaskMapper<? extends Message, ? extends Message, ? extends TaskForm> getTaskMapper() {
            return new MyTaskMapper();
        }

    }
}

public interface TaskMapper<I extends Message, O extends Message, F extends TaskForm> {

    public F fillForm(I msgIn, O msgOut, F taskForm);

    public O fillMsgOut(F taskForm);
}

public class MyTaskMapper implements TaskMapper<IncomingMessage, OutgoingMessage, MyTaskForm > {

    public MyTaskForm fillForm(IncomingMessage msgIn, OutgoingMessage msgOut,
            MyTaskForm taskForm) {
        return null;
    }

    public OutgoingMessage fillMsgOut(MyTaskForm taskForm) {
        return null;
    }

}

The problem is a compilation error:

Type mismatch: cannot convert from MapperFactory.MyTaskMapperFactory to MapperFactory

in my MapperFactory here:

if (msgIn.isMyMapper())
            return new MyTaskMapperFactory();

Any ideas how to fix this error?

Of course replacing:

public static <M extends TaskMapper<? extends Message, ? extends Message, ? extends TaskForm>> MapperFactory<M> getMapperFactory(Message msgIn, Message msgOut) {

        if (msgIn.isMyMapper())
            return new MyTaskMapperFactory();

        throw new IllegalStateException("Mapper not found!");
    }

with:

public static MapperFactory<?> getMapperFactory(Message msgIn, Message msgOut) {

        if (msgIn.isMyMapper())
            return new MyTaskMapperFactory();

        throw new IllegalStateException("Mapper not found!");
    }

would work, but that is not the answer that I am looking for.

This seems to be a problem with generic abstract factory pattern in general. Answers providing source samples using custom made-up objects are also welcomed.

Was it helpful?

Solution

According to Effective Java, 2nd edition, item 28:

If a type parameter appears only once in a method declaration, replace it with a wildcard.

Your getMapperFactory method only uses the type parameter M in the return type. Following this advice gives the following method signature, and the method compiles:

public static MapperFactory<? extends TaskMapper<Message, ? extends Message, ? extends String>> getMapperFactory(Message msgIn, Message msgOut)

EDIT: The more I look at the code, the more I think MapperFactory shouldn't be parameterized. The parameter isn't used in the code here, getTaskMapper returns a TaskMapper.

OTHER TIPS

The return statement works fine with a typecast:

return (BpmMapperFactory<MAPPER>)new Bpm007PrepareDocTaskMapperFactory();

That code will never execute though in its current form, because Bpm007PrepareDocTaskMapper doesn't extend BpmCommonMessageDto, so msgIn cannot possibly be an instance of Bpm007PrepareDocTaskMapper.

My solution would be killing as much of the generics as possible with a fire:

abstract class MapperFactory<M extends TaskMapper<?, ?, ?>> {

    public static MapperFactory<?> getMapperFactory(Message msgIn, Message msgOut) {
        if (msgIn.isMyMapper()) return new MyTaskMapperFactory();
        throw new IllegalStateException("Mapper not found!");
    }

    public abstract M getTaskMapper();
}


class MyTaskMapperFactory extends MapperFactory<MyTaskMapper> {

    @Override
    public MyTaskMapper getTaskMapper() {
        return new MyTaskMapper();
    }

}


interface TaskMapper<I extends Message, O extends Message, F extends TaskForm> {

    public F fillForm(I msgIn, O msgOut, F taskForm); 

    public O fillMsgOut(F taskForm);

}

class MyTaskMapper implements TaskMapper<IncomingMessage, OutgoingMessage, MyTaskForm> {

    public MyTaskForm fillForm(IncomingMessage msgIn, OutgoingMessage msgOut, MyTaskForm taskForm) {
        return null;
    }

    public OutgoingMessage fillMsgOut(MyTaskForm taskForm) {
        return null;
    }

}

It's really not necessary to repeat the type parameters of a class in every method that uses it if you don't really care what they are or don't need to constrain them more than the class signature does.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top