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



我可以理解使用泛型类型实例化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")进行未检查的转换。

在这种情况下,这样做是安全的,因为您永远不会尝试将raw类型用作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>");
}

相关内容

  • 没有找到相关文章