Class<T> 会让你调用newInstance(),这会返回一个T。如果然后将其与wildcard generics 结合使用,理论上您可以指定Class<? extends List<LoggingEvent>> 并创建任何实现List<LoggingEvent> 接口的类型。
然而这是第一个问题:你cannot use a parameterized type with the class literal(即LinkedList<LoggingEvent>.class不会编译)。因此,您必须放宽您的方法/构造函数参数,以仅将通配符绑定在 List 的原始类型上,例如:Class<? extends List>。
所以现在当您创建List 时,您必须将其转换为正确的泛型类型。这意味着您需要使用@SuppressWarnings("unchecked") 执行unchecked conversion。 在这种情况下这样做是安全的,因为您永远不会尝试将该原始类型用作除 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 的类型参数。但是,即使您确实检查了 type 参数,您也无法检查所有可能性,并且 API 仍然可能会中断(例如,如果用户通过类获取只读 List<LoggingEvent>)。
例如,即使是这样简单的事情也会导致(对于 API 用户来说很奇怪,可能无法追踪)运行时异常:
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>");
}