Java泛型和混合类型的容器

时间:2014-11-23 11:38:32

标签: java generics

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

我有这样的事情:

interface Processor<T> {

    T process(T value);
}

class StringProcessor implements Processor<String> {

    String process(String value);
}

然后我有另一个使用处理器的参数化类或接口。我想确保我们设置给Element的处理器可以处理这种类型的元素(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,但您认为有一种方法可以使用泛型来干净地管理它吗?

我已经阅读了#34; Effective Java&#34;中的模式,但是当我从容器中读取元素时,我无法预测返回类型将是什么。

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

欢迎任何想法?

此致

吉勒

2 个答案:

答案 0 :(得分: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字段。

更改现有elementType上的Element是有问题的,因为可能存在对旧类型的引用。例如,如果您对某个对象有Element<Sting>引用,并且在代码的其他部分中将elementType更改为Integer,那么您在使用时会获得ClassCastException即使代码的那部分没有强制转换,也是第一个引用。这就是“未经检查的强制转换”编译警告告诉您的内容。

如果您想要这样做,您可以编写类似于getTyped的方法:

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

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

答案 1 :(得分:0)

试试这个

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