使用Java中的泛型避免警告

时间:2016-01-08 22:51:33

标签: java generics

我有一些带有泛型的代码片段,它被剥离到它的核心看起来像这样。有一些简单的通用接口

public interface Doubler<T> {
    T doubling (T param);
}

并且有工厂方法返回此接口的不同实例

public class DoublerFactory {

    public static void main(String[] args) {
        Doubler<String> input = produceDoubler("String");
        System.out.println(input.doubling(args[0]));
    }

    public static <T> Doubler<T> produceDoubler(String type) {
        Doubler result;
        switch (type) {
            case "Integer" : result =  new IntDoubler(); break;
            case "Float" : result = new FloatDoubler(); break;
            case "String" : result = new StringDoubler(); break;
            default: result = null;
        }
        return result;
    }


    static class IntDoubler implements Doubler<Integer> {
        @Override
        public Integer doubling(Integer param) {
            return param*2;
        }
    }

    static class FloatDoubler implements Doubler<Float> {
        @Override
        public Float doubling(Float param) {
            return param*2;
        }
    }

    static class StringDoubler implements Doubler<String> {
        @Override
        public String doubling(String param) {
            return param + param;
        }
    }
}

除了工厂方法“produceDoubler”中的最后一行产生“未选中分配”警告外,一切正常。我实际上理解为什么 - 我无法弄清楚如何编写这个片段,以便Java编译器对分配完全满意。

2 个答案:

答案 0 :(得分:3)

public static <T> Doubler<T> produceDoubler(String type) {

此签名所说的是,有人可以针对来电者想要的任何类型produceDoubler致电T完全而不管{{1} },type可以返回相应的Doubler。当然,它不能。但这可能会做一些愚蠢的事情

produceDoubler

当然没有意义。没有明智的方法来加倍颜色,并且类型和字符串之间没有关系。

使编译器满意的唯一方法是返回Doubler<Color> doubler = produceDoubler("Integer"); ,但当你试图使用 {{1}时,这当然会迫使你做不安全的强制转换。 }。

实际上不是一种类型安全的方法 - 将任意Doubler<?>连接到不同的类型。唯一类型安全的替代方案是提升Doubler - 在顶层做类似

String

...无需在任何时候尝试存储非具体类型的倍增器。

答案 1 :(得分:0)

工厂方法的可能签名将要求所需类型的类:

public class DoublerFactory() {
    public <T> Doubler<T> produceDoubler(Class<? extends T> cls) {
        // This accepts both Class objects designating the primitive type and its object
        // wrapper, although obviously the result always uses the Object-derived wrapper
        if (Integer.TYPE.equals(cls) || Integer.class.equals(cls))
            return new IntDoubler();
        if (Float.TYPE.equals(cls) || Float.class.equals(cls))
            return new FloatDoubler();
        if (String.class.equals(cls))
            return new StringDoubler();
        throw new IllegalArgumentException();
    }
}

// User code
DoublerFactory df = //...
Doubler<Float>  flt = df.produceDoubler(Float.class);
Doubler<String> str = df.produceDoubler(String.class);

这样,您可以保持代码的编译时类型安全性,同时仍然可以证明工厂接口。

不同的实现可以使用Class<?>键映射到Supplier<? extends Doubler<?>>,允许您在运行时添加/删除映射。您将再次进行未经检查的演员表,但这次可以安全地进行演绎:

public class DoublerFactory() {
    private Map<Class<?>, Supplier<? extends Doubler<?>> doublerMakers;

    public DoublerFactory() {
        doublerMakers = new HashMap<>();
        addReg(Integer.TYPE, IntDoubler::new);
        addReg(Integer.class, IntDoubler::new);
        addReg(Float.TYPE, FloatDoubler::new);
        addReg(Float.class, FloatDoubler::new);
        addReg(String.class, StringDoubler::new);
    }

    // Using this function instead of directly calling Map.put in the constructor
    // allows the compiler to check that the concordancy in T is respected
    private <T> void addReg(Class<? extends T> cls, Supplier<? extends Doubler<T>> maker) {
        doublerMakers.put(cls, maker);
    }

    public <T> Doubler<T> produceDoubler(Class<? extends T> cls) {
        Supplier<? extends Doubler<?>> maker = doublerMakers.get(cls);
        if (maker == null) throw new IllegalArgumentException();
        // The cast here is unavoidable, but we know that if all mappings are inserted 
        // using the addReg() method, then the result is type safe.
        @SuppressWarnings("unchecked")
        Doubler<T> ret = (Doubler<T>)(Doubler)maker.get();
        return ret;
    }
}