Search code examples
javagenericsfactory-pattern

Generic factory method in Java


I know that there a couple of threads for this topic but I could not figure out where my problem is. Reading up and down the search results I am more puzzled than ever. I hope I can make my problem clear.

I have a message type hierarchy and I have a generic processor hierarchy for these messages. This I have running so far. Now I want to implement a factory method for the processors and get stuck with the generic approach I have chosen. Please have a look at my example:

abstract class Message
{
    abstract int getTest();
}

class MyMessage1 extends Message
{
    @Override
    int getTest()
    {
        return 1;
    }
}

class MyMessage2 extends Message
{
    @Override
    int getTest()
    {
        return 2;
    }
}

interface Processor<T extends Message>
{
    void initialize(final T p_message);

    void doWork();
}

abstract class BaseMessageProcessor<T extends Message> implements Processor<T>
{
    private T message;

    @Override
    public void initialize(T p_message)
    {
        message = p_message;
    }

    protected T getMessage()
    {
        return message;
    }
}

class MyMessage1Processor extends BaseMessageProcessor<MyMessage1>
{
    @Override
    public void doWork()
    {
        // perfectly valid assignment
        MyMessage1 msg = getMessage();
        // do something
    }
}

class MyMessage2Processor extends BaseMessageProcessor<MyMessage2>
{
    @Override
    public void doWork()
    {
        // perfectly valid assignment
        MyMessage2 msg = getMessage();
        // do something
    }
}

So far this is valid and working as expected. But now comes the factory:

class ProcessorFactory
{
    <T extends Message> Processor<T> createProcessor(final Class<T> p_msgType)
    {
        if (p_msgType.equals(MyMessage1.class))
        {
            // Type mismatch: cannot convert from MyMessage1Processor to Processor<T>
            return new MyMessage1Processor();
        }
        else if (p_msgType.equals(MyMessage2.class))
        {
            // Type mismatch: cannot convert from MyMessage2Processor to Processor<T>
            return new MyMessage2Processor();
        }
        else
        {
            throw new Exception("Unsupported message type: " + p_msgType);
        }
    }
}

Maybe it's a stupid failure I am making here but I can't see it. If someone could give me a hint I would be thankful.

Regards

Sebastian

EDIT: OK my fault. The problem is that I get the compiler errors stated as comments as 'default locale' mentioned (e.g., for the second return statement I get the compile error):

Type mismatch: cannot convert from MyMessage2Processor to Processor


Solution

  • To understand why you are failing, you have to understand how type erasure works:

    While you clearly know you are doing things right, the compiler doesn't and, most importantly, can't know it, for design, because the information has been cancelled

    Let me try to further explain this concept: because of type erasure, the type T is turned at compile time into a java.lang.Object and there is no compile-time information for the Java compiler that says that

    if a.getClass().equals(MyClass.class) then a.getClass() will be of type MyClass<MyClass>


    Important: please avoid to write this code as much as you can because that would lead to horrible design. If you do not want to die in the hell of bugs and unmaintanable code, do not force type-safe languages to use logical evidence of type safety and not compile type evidence. If you forces the type safety by casts in the compiler, you risk that one day you change the class structure and you forget to change the factory, everything compiles fine but at runtime you get a BOOM.

    A factory method is in generally a pattern for hiding from the caller the logic of producing an object and if you end up with such a situation, it probably means that you are not using generics correctly. It is much better to write one more class and having a clean design.

    Maybe you can add an additional question on : how to implement this pattern in a type-safe way?