2016-01-08 48 views
2

我有一些泛型代码片段被剥离到它的核心看起来像这样。有微不足道的通用接口在Java中避免使用泛型的警告

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编译器完全满意分配。

+1

不要对'String'值进行操作。使用'类'与路易的建议[这里](http://stackoverflow.com/questions/9088782/enforce-generic-types-in-maps-in-relation-to-key-value-generics)。 –

回答

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

什么这个签名说的是,有人可以拨打produceDoubler任何类型T呼叫者想,完全不论type,并produceDoubler可以返回适当的倍增。当然,它不能。但是,这可以做一些愚蠢的事情,如

Doubler<Color> doubler = produceDoubler("Integer"); 

这当然没有任何意义。没有一种方法可以使颜色加倍,并且类型和字符串之间没有关系。

使编译器高兴将返回Doubler<?>的唯一方法,但是这当然会力,当你试图使用Doubler你做不安全的转换。

还有不是实际上是一种类型安全的方式来做到这一点 - 将任意String s连接到不同类型。唯一的类型安全的替代方案是解除了switch - 在顶层事做像

switch (type) { 
    case "Double": { 
     Doubler<Double> doubler = new DoubleDoubler(); 
     // use the doubler for everything you need to use it for 
    } 
    ... 
} 

...没有尝试存储一个非具体类型的倍增器在任何时间。

0

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

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; 
    } 
} 
+0

这实际上没有为我编译以下错误:错误:(11,49)java:不兼容的类型:org.test.DoublerFactory.IntDoubler无法转换为org.test.Doubler 等等。 –

+0

你是完全正确的,我太在意另一个答案,即使用String键对用户代码没有类型安全性。 *在工厂里,你总是会有一些不加控制的投射,我担心。我的第一个示例将需要它在与您的问题相同的位置(与您的代码相比,唯一的优势是编译器强制用户代码使用正确的变量类型)。对不起,在这方面不清楚。 –