2014-11-23 59 views
1

我有一个关于在容器中使用混合泛型类型的问题。Java泛型和混合类型的容器

我有这样的事情:

interface Processor<T> { 

    T process(T value); 
} 

class StringProcessor implements Processor<String> { 

    String process(String value); 
} 

然后我有一个使用的处理器的另一个参数类或接口。我希望确保我们设置成元素的处理器可以处理这种类型(T)的元素

class Element<T> { 

     <U> void setProcessor(U extends Processor<? extends T> processor); 
} 

所有这一切工作正常。当我必须将我的元素放入一个容器时,我的问题就来了。该容器可以包含不同类型的元素(Element<String>Element<Integer>,...)。当我从容器中取出元素时,我无法为元素指定一个新的calcultor,因为使用?使我失去了打字。

List<Element<?>> container = new ArrayList<Element<?>>(); 
Element<String> strElement =new Element<String>(); 
strElement.setProcessor(new StringProcessor()); 
Element<Integer> intElement =new Element<Integer>(); 
intElement.setProcessor(new IntegerProcessor()); 

container.add(strElement); 
container.add(intElement); 

但是:

Element<?> e = container.get(0); 
e.setProcessor(new StringProcessor()); // This does not compile because the the  compiler is expecting a processor of type U extends Processor<#1of?> and not a StringProcessor. 

这适用于Rawtypes但是你觉得有与泛型干净管理这个办法?

我已经从“Effective Java”中读取了模式,但是当我从容器中读取元素时,我无法预测返回类型是什么。

迄今为止发现的唯一修复方法是使Element不是通用的,但是通过这样做,我失去了类型安全性。

有什么想法欢迎?

问候

吉勒

+0

你'Element'类没有编译。你能提供一个有效的实现吗? – 2014-11-23 11:44:28

+0

你不能像你现在拥有它。通常的模式是在处理器上有一个'boolean canProcess(Object o)'方法。然后检查这个方法并强制演员。 – 2014-11-23 11:49:40

回答

0

由于Element<?> e = container.get(0);可能实际上导致Element用于任何类型的Processor它不能够设定该字段在它为任何特定类型。一旦将不同类型混合在一起,就无法以静态类型的方式获取该信息。

正如Boris所说,您可以实现某种运行时检查。为此,对象必须在运行时维护他们可以处理的类型的信息。许多变种是可能的。例如:

class Element<T> { 
    public Class<T> elementType; 

    <U extends Processor<? extends T>> void setProcessor(U processor) {} 

    @SuppressWarnings("unchecked") 
    public <E> Element<E> getTyped(Class<E> type) { 
     if (type == elementType) return (Element<E>) this; 
     else return null; 
    } 
} 

Element<?> e = new Element<String>(); 
Element<String> es = e.getTyped(String.class); 
if (es != null) es.setProcessor(new StringProcessor()); 

你得到在getTyped投一个未检查的警告,但它是安全的压制它,因为你检查的类型在运行时匹配。

编辑:关于更改elementType

在创建Element它可能有一个具体的类型,你可以设置elementType场没有问题。

在现有的Element上更改elementType存在问题,因为可能存在对旧类型的引用。例如,如果对对象有一个Element<Sting>引用,并在代码的其他部分将elementType更改为Integer,则即使在代码的该部分中没有强制转换时,也会在使用第一个引用时得到ClassCastException。这是“Unchecked cast”编译警告告诉你的。

如果你想这样做,反正你可以写类似getTyped的方法:

@SuppressWarnings("unchecked") 
public <E> Element<E> getTypedAndClear(Class<E> type) { 
    elementType = null; 
    return (Element<E>) this; 
} 

之后,您可以设置elementType的类型E的类对象。

+0

非常感谢。你如何设置元素中的元素类型 Gilles 2014-11-24 09:03:31

+0

我编辑了我的答案并添加了一些关于该答案的文本。既然你是这个网站的新手,我也想提醒你,如果你觉得它有用,就会提出答案,如果它解决了你的问题,就接受它。 – Lii 2014-11-24 09:47:43

+0

非常感谢。我使用了Boris解决方案(在处理器上添加了一个方法)和你的方法(向Element添加了一个类型,以便我可以验证类型安全性)。再次感谢 – Gilles 2014-11-24 19:51:12

0

试试这个

Element<String> e = container.get(0); // chnages ? to Stirng 
e.setProcessor(new StringProcessor());