简体   繁体   English

为什么在运行时创建泛型 List 不绑定正确的数据类型?

[英]Why creating generic List not binding the right data type at runtime?

I am trying to create a generic method that returns a generic list with a given data type.我正在尝试创建一个通用方法,该方法返回具有给定数据类型的通用列表。

I have passed Integer as data type and I filled with String with no issue at runtime.我已将Integer作为数据类型传递,并且在运行时没有问题地填充了String

My question why I didn't receive ClassCastException during the Runtime?我的问题是为什么我在运行时没有收到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;
    }

, output , output

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

Due to type erasure , not every violation of the generic type system is identified at runtime.由于类型擦除,并非所有对泛型类型系统的违规都会在运行时被识别出来。 This situation is also called heap pollution .这种情况也称为堆污染 But you should have received “unchecked” warnings from the compiler.但是您应该已经收到了来自编译器的“未经检查”的警告。

But your assumption that you create a List<Integer> is wrong anyway.但是您创建List<Integer>的假设无论如何都是错误的。 You are passing Integer.class as argument for the T clazz parameter, hence, T is not Integer but Class<Integer> .您将Integer.class作为T clazz参数的参数传递,因此, T不是Integer而是Class<Integer>

A correct generic declaration would be:正确的通用声明是:

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

Now that the program is free of “unchecked” warnings, you can be sure that no heap pollution will occur at runtime.现在该程序没有“未经检查”的警告,您可以确定在运行时不会发生堆污染。 If you want more guarantees, you can use如果你想要更多的保证,你可以使用

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

However, when you listen and respond to the compiler warnings, you don't need the additional runtime checks.但是,当您收听并响应编译器警告时,您不需要额外的运行时检查。

As you have declared T as <T> the runtime type-checking of (T) is against Object .正如您已将T声明为<T>(T)的运行时类型检查是针对Object Therefore, the cast from a List is fine.因此,列表中的演员List很好。

List is declared as List<E> , so there can be no additional internal runtime type-checking from List.add . List被声明为List<E> ,因此无法从List.add进行额外的内部运行时类型检查。 Likewise for List.toString . List.toString

You will, however, get warnings (so long as the lint option is enabled in your compiler).但是,您将收到警告(只要您的编译器中启用了 lint 选项)。 Don't ignore the warnings.不要忽视警告。

You can use clazz.cast in place of T , though T cannot usefully be a parameterised type.您可以使用clazz.cast代替T ,尽管T不能是有用的参数化类型。

Generics provide type checks at compile time and are erased by the compiler during the Type Erasure . Generics 在编译时提供类型检查,并在类型擦除期间由编译器擦除 So after the type erasure your method will look something like this:因此,在类型擦除之后,您的方法将如下所示:

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

And your main method will look as follows:您的main方法如下所示:

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

So there's no reason for a ClassCastException to be thrown, because you are not trying to do any Integer specific actions to the elements of your list.因此,没有理由ClassCastException ,因为您没有尝试对列表元素执行任何Integer特定操作。 But you will get it as soon as you do, for example:但是你会尽快得到它,例如:

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

because here it will try to cast ArrayList to Integer to call intValue() .因为在这里它将尝试将ArrayListInteger以调用intValue()

Conclusion: using generics along with unchecked casting is quite useless.结论:使用 generics 以及未经检查的铸造是非常没用的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM