这是一个值得了解的 Java 泛型小技巧。
当您处理已知一致但在代码中不明确的类型参数时,就会出现问题。如果您正在处理不完全类型化的集合,这很常见。
为了使事情更清楚,我将使用以下示例:考虑一个将各种值从一个地方传输到另一个地方的系统(也许它是一个调度程序在系统内发送不同类型的消息)。
我们可能有一个可以提供和接收某些消息类型的接口:
public interface Connection<Type>
{
Type read();
void write(Type value);
}
我们的调度器可能看起来像这样:
class Scheduler
{
public void process(Collection<Connection<?>> cnxs)
{
for (Connection<?> cns: cnxs) {
cnx.write(cnx.read);
}
}
}
(注意这是简写,我们在这里使用它是因为 cnxs 集合包含一个具有各种不同类型参数的 Connections)。
不幸的是,这不会编译!Eclipse with Java 1.6 给出的错误是“Connection 类型的方法 write(capture#2-of ?) 不适用于参数 (capture#3-of ?)”。
无法编译的原因是 Connection 返回的值的类型参数和它将接收的值的类型参数被分开处理。每个都被视为“捕获?” 这意味着“对象的某个子类”。然后编译器(可以理解)说“我不能将'对象的子类 X'发送到期望'对象的子类 Y'的方法,因为我不知道它们是否是同一个子类”。
为了完成这项工作,我们需要显式地引入公共类型参数。不幸的是,以下代码或类似代码不起作用(据我所知)。没有办法在代码块中间引入类型参数(我们这里真正想要的是更好地支持多态性):
class Scheduler
{
public void process(Collection<Connection<?>> cnxs)
{
// syntax error!
for (<E> Connection<E> cns: cnxs) {
E value = cnx.read();
cnx.write(value);
}
}
}
但是我们可以做的是添加一个引入新类型参数的辅助方法:
class Scheduler
{
public void process(Collection<Connection<?>> cnxs)
{
for (Connection<?> cnx: cnxs) {
helper(cnx);
}
}
private <E> void helper(Connection<E> cnx)
{
E value = cnx.read();
cnx.write(value);
}
}
这就是我们想要的!代码验证、编译和运行。
总而言之:有时您可能会“丢失”显式泛型类型参数(通常是因为您正在处理不同类型的集合)。您可以通过添加额外的辅助方法来重新引入该类型参数。