我有一个关于在容器中使用混合泛型类型的问题。
我有这样的事情:
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不通用,但通过这样做,我失去了类型安全性。
欢迎任何想法?
此致
吉勒
答案 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());