使用具体类型的类创建列表

时间:2015-08-11 01:24:19

标签: java list generics reflection

我可以理解使用泛型类型实例化List对象的示例。我目前的家庭作业涉及为Log4j创建Appender,将日志存储在列表中。其中一个要求是用户可以为构造函数指定List的具体实现,我通过接受他们创建的List来完成此任务。

有没有办法让它们能够提供他们想要使用的Class<T>实现List,我的构造函数会实例化它的新实例?

2 个答案:

答案 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>");
}