Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generics abstract factory issue

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.

like image 336
Funky coder Avatar asked Feb 08 '12 10:02

Funky coder


3 Answers

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.

like image 174
TDJoe Avatar answered Nov 15 '22 23:11

TDJoe


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.

like image 1
Boann Avatar answered Nov 15 '22 23:11

Boann


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.

like image 1
millimoose Avatar answered Nov 15 '22 22:11

millimoose