Search code examples
javagenericsnested-generics

Declaring method returning generic subtype from a declaring type


I'm trying to create a method in a factory class. The return of the subtype must be the same type as the declared argument.

The method declaration worked, but when I try to use, the method is not returning the expected type.

Here is a class exemplifying my problem:

import java.util.HashMap;

/**
 *
 * @author Marcos Martinewski Alves
 */
public class FooParserFactory {

    private static final HashMap<Class<? extends Foo>, FooParser<? extends Foo>> fooParsers = new HashMap();

    public static FooParser<? extends Foo> getFooParser(Class<? extends Foo> cls) {
        initParsers();
        FooParser<? extends Foo>  parser = fooParsers.get(cls);
        if (parser == null) {
            throw new RuntimeException("FooParser not found for class "+cls);
        }
        return parser;
    }

    private static void initParsers() {
        if (fooParsers.isEmpty()) {
            // populate fooParsers hashmap
        }
    }

}

The foo interface

public interface Foo {

}

The foo implementation

public class FooImpl implements Foo {

}

The FooParser

public interface FooParser<T extends Foo> {

    public T parse(Object object);

}

And where the problem occurs

public class FooParserUsage {

    public void useFooParser(Object source) {
        FooImpl fooImpl = FooParserFactory.getFooParser(FooImpl.class).parse(source); // here
    }

}

I'm using NetBeans IDE 8.1 and I'm getting the following error:

incompatible types: CAP#1 cannot be converted to FooImpl where CAP#1 is a frash type-variable CAP#1 extends object from capture of ?

Is there a way to do something like this?

Many thanks in advance


Solution

  • Just because <? extends Foo> looks the same as <? extends Foo> doesn't mean they are compatible :-)

    You could try to reformulate it as follows:

    public static <T extends Foo> FooParser<T> getFooParser(Class<T> cls) {
    //            ^^^^^^^^^^^^^^^           ^                     ^
    //            introduce a type       now these two are "compatible"
    //            variable
        initParsers();
    
        // This line will however require a cast. Judge for yourself if it is safe.
        FooParser<T> parser = (FooParser<T>) fooParsers.get(cls);
        if (parser == null) {
            throw new RuntimeException("FooParser not found for class " + cls);
        }
        return parser;
    }