我可以理解使用泛型类型实例化List
对象的示例。我当前的家庭作业涉及为Log4j创建一个Appender
,它将日志存储在一个列表中。其中一个要求是用户可以为构造函数指定List
的具体实现,我通过接受他们创建的List
来实现这一点。
是否有一种方法可以使它,以便他们可以提供Class<T>
实现List
,他们想要使用和我的构造函数将实例化它的新实例?
如果我理解你的问题,那么是的;我能想到的最简单的方法,是一个接受Collection
的构造函数。例如:ArrayList(Collection)
.
List<SomeType> copyList = new ArrayList<>(original);
或者,您可能希望在给定项T
的情况下返回List<T>
。比如Collections.singletonList(T)
SomeType t = // ...
List<SomeType> al = Collections.singletonList(t);
Class<T>
将允许您调用newInstance()
,这将返回一个T
。如果将它与通配符泛型结合使用,理论上可以指定Class<? extends List<LoggingEvent>>
并创建实现List<LoggingEvent>
接口的任何类型。
然而,这是第一个问题:你不能对类文字使用参数化类型(即LinkedList<LoggingEvent>.class
将无法编译)。因此,您必须放松方法/构造函数参数,只将通配符绑定到List
的原始类型上,如:Class<? extends List>
。
所以现在当您创建List
时,您必须将其强制转换为正确的泛型类型。这意味着您需要使用@SuppressWarnings("unchecked")
进行未检查的转换。 †
List<LoggingEvent>
以外的任何其他泛型类型。最终的实现看起来像这样:
class LogStore {
private List<LogLine> loggingEvents = null;
public LogStore(Class<? extends List> clazz) {
try {
@SuppressWarnings("unchecked")
List<LogLine> logStoreList = clazz.newInstance();
this.loggingEvents = logStoreList;
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
†在这种情况下,如果您向用户提供一组提示让他们选择标准的List<LoggingEvent>
实现,那么这样做可能是安全的,但是如果他们打算将其作为API使用,而您无法控制他们传递的Class<? extends List>
,那么您有两个选择:要么让API在运行时在不可预测的地方失败,要么尝试检查list的类型参数。然而,即使你检查了类型参数,你也不能检查所有的可能性,API仍然可能会中断(例如,如果用户将类传递给只读的List<LoggingEvent>
)。
new LogStore(IntList.class);
// Used with IntList defined as ...
public class IntList extends ArrayList<Integer> {
@Override public Integer remove(int index) { return super.remove(index); }
}
如果您选择了后一个选项,那么您将需要执行如下操作来检查它是否确实是List<LoggingEvent>
:
public LogStore(Class<? extends List> clazz) {
assertListTypeArgsValid(clazz);
// ... the rest of the above method implementation ...
}
private void assertListOk(Class<? extends List> clazz) {
boolean verified = false;
for (Type intr : clazz.getGenericInterfaces()) {
if (!(intr instanceof ParameterizedType)) continue;
ParameterizedType pIntr = (ParameterizedType)intr;
if (pIntr.getRawType().getTypeName() != "java.util.List") continue;
Type[] typeArgs = pIntr.getActualTypeArguments();
if (typeArgs.length != 1) break;
Class<?> tac = (Class<?>)typeArgs[0];
verified = tac.isAssignableFrom(LoggingEvent.class);
if (!verified) throw new IllegalArgumentException("clazz must be a List<LoggingEvent>, and is a: "
+ pIntr.getTypeName());
break;
}
if (!verified) throw new IllegalArgumentException("clazz must be a List<LoggingEvent>");
}