5

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.

Wojciech Owczarczyk
  • 5,595
  • 2
  • 33
  • 55
  • `MAPPER` is not a good class name following Java conventions. – Hauke Ingmar Schmidt Feb 13 '12 at 11:52
  • Please make your class names as small as possible (but still clear). And rename `MAPPER` to `Mapper`? Weird/long class names just confuse/clutter up the question *and* the answers. – Bohemian Feb 13 '12 at 12:01
  • @his MAPPER is not a class name. It is the name of generic Type such as T in HashMap – Wojciech Owczarczyk Feb 13 '12 at 12:06
  • It is still a type name. All capital is for constants in Java. – Hauke Ingmar Schmidt Feb 13 '12 at 12:08
  • @his - no it is not, nevermind... The convention is to use only single letters as BalusC stated here http://stackoverflow.com/questions/2900881/generic-type-parameter-naming-convention-for-java-with-multiple-chars so I changed my code to follow the conventions. – Wojciech Owczarczyk Feb 13 '12 at 12:13
  • It is a type parameter, a type variable: http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.1.2 For type parameters the convention is a single letter, that is right. – Hauke Ingmar Schmidt Feb 13 '12 at 12:20

3 Answers3

4

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.

TDJoe
  • 1,388
  • 1
  • 10
  • 15
  • 2
    Even shorter, the signature can just be `public static MapperFactory> getMapperFactory()`. The method doesn't constrain any of the type parameters more than the class signature for `MapperFactory` does. The same goes for `public abstract TaskMapper, ?, ?> getTaskMapper()`. (It could also be `public abstract M getTaskMapper()`, can't tell what the intent behind the type parameter is.) *"When in doubt, use less generics"* is probably a good rule of thumb. – millimoose Feb 14 '12 at 15:39
1

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.

Boann
  • 48,794
  • 16
  • 117
  • 146
1

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.

millimoose
  • 39,073
  • 9
  • 82
  • 134