我可以理解使用泛型类型实例化List
对象的示例。我目前的家庭作业涉及为Log4j创建Appender
,将日志存储在列表中。其中一个要求是用户可以为构造函数指定List
的具体实现,我通过接受他们创建的List
来完成此任务。
有没有办法让它们能够提供他们想要使用的Class<T>
实现List
,我的构造函数会实例化它的新实例?
答案 0 :(得分:2)
如果我理解你的问题,那么是的;我能想到的最简单的方法是使用Collection
的构造函数。例如,ArrayList(Collection)
。
List<SomeType> copyList = new ArrayList<>(original);
或者,或许,您希望在给定项目List<T>
的情况下返回T
的内容。像Collections.singletonList(T)
SomeType t = // ...
List<SomeType> al = Collections.singletonList(t);
答案 1 :(得分:1)
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的类型参数。但是,即使您确实检查了类型参数,也无法检查所有可能性,并且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>");
}