【问题标题】:Why creating generic List not binding the right data type at runtime?为什么在运行时创建泛型 List 不绑定正确的数据类型?
【发布时间】:2020-04-28 22:15:20
【问题描述】:

我正在尝试创建一个通用方法,该方法返回具有给定数据类型的通用列表。

我已将 Integer 作为数据类型传递,我填充了 String,在运行时没有问题。

我的问题是为什么我在运行时没有收到ClassCastException

    public static void main(String[] args) {
        List<Integer> list = (List<Integer>) generic(Integer.class);
        System.out.println(list);
    }

    private static <T> List<?> generic(T clazz) {
        List<T> list = new ArrayList<>();
        list.add((T) Arrays.asList("create", "generic"));
        list.add((T) Arrays.asList("create", "generic2"));
        return list;
    }

,输出

[[create, generic], [create, generic2]]

【问题讨论】:

标签: java generics collections


【解决方案1】:

由于type erasure,并非所有对泛型类型系统的违规都会在运行时被识别。这种情况也称为堆污染。但是你应该已经收到了来自编译器的“未经检查”的警告。

但是您创建List&lt;Integer&gt; 的假设无论如何都是错误的。您将Integer.class 作为T clazz 参数的参数传递,因此T 不是Integer 而是Class&lt;Integer&gt;

正确的泛型声明应该是:

public static void main(String[] args) {
    List<Integer> list = generic(Integer.class);
    System.out.println(list);
}

private static <T> List<T> generic(Class<T> clazz) {
    List<T> list = new ArrayList<>();
    return list;
}

现在程序没有“未经检查”的警告,您可以确定在运行时不会发生堆污染。如果你想要更多的保证,你可以使用

public static void main(String[] args) {
    List<Integer> list = generic(Integer.class);
    System.out.println(list);
}

private static <T> List<T> generic(Class<T> clazz) {
    List<T> list = Collections.checkedList(new ArrayList<>(), clazz);
    list.add((T) Arrays.asList("create", "generic")); // will fail at runtime
    list.add((T) Arrays.asList("create", "generic2"));
    return list;
}

但是,当您侦听并响应编译器警告时,您不需要额外的运行时检查。

【讨论】:

    【解决方案2】:

    正如您已将T 声明为&lt;T&gt;(T) 的运行时类型检查与Object 相悖。因此,List 的演员表很好。

    List 被声明为List&lt;E&gt;,因此不能从List.add 进行额外的内部运行时类型检查。 List.toString 也是如此。

    但是,您会收到警告(只要您的编译器中启用了 lint 选项)。不要忽视警告。

    您可以使用clazz.cast 代替T,尽管T 不能成为有用的参数化类型。

    【讨论】:

      【解决方案3】:

      泛型在编译时提供类型检查,并在Type Erasure 期间被编译器删除。因此,在类型擦除之后,您的方法将如下所示:

      private static List generic(Object clazz) {
          List list = new ArrayList();
          list.add((Object) Arrays.asList("create", "generic"));
          list.add((Object) Arrays.asList("create", "generic2"));
          return list;
      }
      

      您的main 方法如下所示:

      List list = (List) generic(Integer.class);
      System.out.println(list);
      

      因此没有理由抛出ClassCastException,因为您没有尝试对列表中的元素执行任何Integer 特定操作。但是你会尽快得到它,例如:

      list.forEach(integer -> System.out.println(integer.intValue()));
      

      因为这里它会尝试将ArrayList 转换为Integer 以调用intValue()

      结论:使用泛型和未经检查的强制转换是完全没用的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多