Java8 Supplier接口提供正确的类型化实例

时间:2018-05-02 02:42:07

标签: java generics

如何使用Java8 Supplier接口重写此工厂方法以提供正确的类型化实例?

我是一个扩展Map的简单界面:

public interface Thingy<K, V> extends Map<K, V> {}

然后我有一个ThingyFactory类,其中包含Thingy所有实现类名的列表:

public final class ThingyFactory {
    Map<String, Class<Thingy<?, ?>>> thingyclasses = new ConcurrentHashMap<>();
    .....

    @SuppressWarnings("unchecked")
    public <K, V> Thingy<K, V> getInstance(String classname) throws ThingyException {
        Thingy<K, V> thingy;
        try {
            thingy = (Thingy<K, V>) thingyclasses.get(classname).newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new ThingyException("Something bad happened: ", e.toString());
        }
        return thingy;
    }
}

我非常确定我可以优雅地执行此操作,并且不使用供应商界面使用SuppressWarnings和类加载器,但我似乎无法使模式完全正确。任何指导赞赏!

2 个答案:

答案 0 :(得分:1)

因为您在Thingy中使用通配符作为thingyclasses的通用类型,所以您实际上是在说: Thingy的类型可以是任何 ;但是,这会禁止编译器推断有关类型的任何内容,因此需要显式转换。您可以使用Supplier略微改进它,但您仍会收到关于未经检查的广告投放的警告:

class ThingyFactory {
    private Map<String, Supplier<Thingy<?, ?>>> providers = new ConcurrentHashMap<>();

    @SuppressWarnings("unchecked")
    public <K, V> Supplier<Thingy<K, V>> getInstance(String classname) {
        return () -> (Thingy<K, V>) providers.get(classname);
    }
}

如果您希望它是类型安全的,那么您需要完全重新设计它,并且只有在编译器能够推断出类型时才使用通配符。

答案 1 :(得分:0)

有趣的挑战。我认为这就是你要找的东西:

public class Main {

    public static void main(String[] args) {
        ThingyFactory<String, String> factory = new ThingyFactory<>();
        Thingy<String, String> instance = factory.getInstance("ThingyImpl");
        System.out.println(instance.clazzName());
        Thingy<String, String> otherInstance = factory.getInstance(ThingyImpl.class);
        System.out.println(instance.clazzName());
    }
}

interface Thingy<K, V> extends Map<K, V> {
    //added this method for testing purpuses
    String clazzName();
}

//extending HashMap so I don't have to implement Map's methods
class ThingyImpl<K, V> extends HashMap<K, V> implements Thingy<K, V> {
    public String clazzName() {
        return "ThingyImpl";
    }
}

final class ThingyFactory<K, V> {
    private Map<String, Supplier<Thingy<K, V>>> providers = new ConcurrentHashMap<>();

    public ThingyFactory() {
        providers.put("ThingyImpl", () -> new ThingyImpl());
    }

    public Thingy<K, V> getInstance(String classname) {
        return providers.get(classname).get();
    }

    // alternative with Class. 
    // You could change providers to a Map<Class<? extends Thingy>, Supplier<Thingy<K, V>>>
    public Thingy<K, V> getInstance(Class<? extends Thingy> clazz) {
        return providers.get(clazz.getName()).get();
    }
}

当然,对于每个特定的K,V类型对,您都需要一个ThingyFactory,但这是类型安全的。