为什么可以使用反射将Double添加到整数列表中

Edm*_*ang 61 java

为什么这段代码没有任何例外?

public static void main(String args[]) {
    List<Integer> a = new ArrayList<Integer>();
    try {

        a.getClass()
            .getMethod("add", Object.class)
            .invoke(a, new Double(0.55555));

    } catch (Exception e) {
        e.printStackTrace();
    } 
    System.out.println(a.get(0));
}
Run Code Online (Sandbox Code Playgroud)

JB *_*zet 94

泛型是一个编译时间的东西.在运行时,使用常规的ArrayList,无需任何额外检查.由于您通过使用反射向列表中添加元素来绕过安全检查,因此没有任何内容可以阻止将内容Double存储在您的列表中List<Integer>.就像你做的那样

List<Integer> list = new ArrayList<Integer>();
List rawList = list;
rawList.add(new Double(2.5));
Run Code Online (Sandbox Code Playgroud)

如果希望列表在运行时实现类型检查,则使用

List<Integer> checkedList = Collections.checkedList(list, Integer.class);
Run Code Online (Sandbox Code Playgroud)


And*_*hev 35

由于类型擦除 - 没有对泛型的运行时检查,在编译期间删除了类型参数:Java泛型 - 类型擦除 - 何时以及发生了什么.

您可能会感到惊讶,但您不需要使用反射来添加DoubleList<Integer>:

List<Integer> a = new ArrayList<Integer>();
((List)a).add(new Double(0.555));
Run Code Online (Sandbox Code Playgroud)


das*_*ght 23

原因是类型擦除:Integer编译器已知这是s 的列表,而不是JVM.

编译代码后,List<Integer>变为List<Object>,允许基于反射的代码完成而没有错误.

请注意,您自己的代码强烈暗示了其工作原因:

a.getClass()
    .getMethod("add", Object.class) // <<== Here: Object.class, not Integer.class
    .invoke(a, new Double(0.55555));
Run Code Online (Sandbox Code Playgroud)

另请注意,您可以通过一些创造性的使用铸造来实现相同的邪恶结果,而无需反射.所有这些都是设计决定实现类型擦除的Java泛型的结果.


Naz*_*gul 5

泛型只是java提供的编译时工具.在泛型之前,没有办法在编译时确保从集合中获取的"Object"实例实际上是您期望的类型.我们必须将对象转换为适当的类型才能在代码中使用,这可能会有风险,因为只有在运行时JVM才会抱怨ClassCastException.编译时没有任何东西可以保护我们免受此事的侵害.

泛型通过在编译时在集合中强制执行类型检查来解决此问题.但是关于泛型的另一个重要的事情是它们在运行时不存在.如果您反编译包含List或Map等类型集合的类并查看从中生成的java源代码,那么您将无法在其中找到您的泛型集合声明.由于反射代码在运行时工作并且没有编译时间,因此您不会在那里获得异常.尝试在编译时使用正常的put或add操作执行相同操作,您将收到编译时错误.