【问题标题】:Creating a List using the concrete type's Class使用具体类型的类创建列表
【发布时间】:2015-08-11 01:24:19
【问题描述】:

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

有没有办法让他们可以提供他们想要使用的实现ListClass<T>,并且我的构造函数将实例化它的一个新实例?

【问题讨论】:

    标签: java list generics reflection


    【解决方案1】:

    如果我理解你的问题,那么是的;我能想到的最简单的方法是使用Collection 的构造函数。例如,ArrayList(Collection)

    List<SomeType> copyList = new ArrayList<>(original);
    

    或者,您可能想要在给定项目 T 的情况下返回 List&lt;T&gt; 的东西。类似Collections.singletonList(T)

    SomeType t = // ...
    List<SomeType> al = Collections.singletonList(t);
    

    【讨论】:

    • 更重要的是,我知道我想要一个 LoggingEvents 列表,但我们打算这样做,以便任何想要使用我们的 appender 的人来指定他们想要使用的列表类型。因此,我将私有列表声明为 List,但此 List 的实现可能是 ArrayList、LinkedList 或其他任何内容,具体取决于调用构造函数时指定的内容。
    • @Syzorr 那你program to the interface.
    • 是的 - 我也一直在和朋友讨论这个问题,我可能用词非常糟糕。我现在才真正了解泛型,而且我已经在“对接口进行编程”我的目标是什么,我终于想通了: public MemoryAppender(Class clazz) throws Exception { super(); eventsLog = clazz.newInstance();仍在努力,但这就是我的目标。感谢您的反馈。
    【解决方案2】:

    Class&lt;T&gt; 会让你调用newInstance(),这会返回一个T。如果然后将其与wildcard generics 结合使用,理论上您可以指定Class&lt;? extends List&lt;LoggingEvent&gt;&gt; 并创建任何实现List&lt;LoggingEvent&gt; 接口的类型。

    然而这是第一个问题:你cannot use a parameterized type with the class literal(即LinkedList&lt;LoggingEvent&gt;.class不会编译)。因此,您必须放宽您的方法/构造函数参数,以仅将通配符绑定在 List 的原始类型上,例如:Class&lt;? extends List&gt;

    所以现在当您创建List 时,您必须将其转换为正确的泛型类型。这意味着您需要使用@SuppressWarnings("unchecked") 执行unchecked conversion在这种情况下这样做是安全的,因为您永远不会尝试将该原始类型用作除 List&lt;LoggingEvent&gt; 之外的任何其他泛型类型。

    最终的实现类似于:

    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&lt;LoggingEvent&gt; 实现,那么这样做可能是安全的,但如果他们要使用这个作为一个 API,你无法控制它们传入哪个Class&lt;? extends List&gt;,那么你有两个选择:要么让 api 在运行时在不可预测的地方失败,要么尝试检查 list 的类型参数。但是,即使您确实检查了 type 参数,您也无法检查所有可能性,并且 API 仍然可能会中断(例如,如果用户通过类获取只读 List&lt;LoggingEvent&gt;)。

    例如,即使是这样简单的事情也会导致(对于 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&lt;LoggingEvent&gt;

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

    【讨论】:

    • "在这种情况下,这样做是安全的..." 除非你提供一个不能是 List&lt;LogLine&gt;List 类。例如如果我有class IntList implements List&lt;Integer&gt;
    • @newacct。我曾假设 OP 提供了“List&lt;LoggingEvent&gt; 选择列表”——如果没有,这将用作 API,那么可能确实需要进一步检查以防止这种荒谬的使用。然而,即使这样也不是万无一失的。 API 用户可以将类传递给只读列表实现。
    猜你喜欢
    • 1970-01-01
    • 2019-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-16
    • 1970-01-01
    • 2012-08-30
    相关资源
    最近更新 更多