我有一个方法应该返回具有给定条目的给定集合类的实例。实现如下所示。
public static <E, C extends Collection<E>> C
initalizeAndAddToCollection(Class<C> clazz, E... entries) {
//Collection object
Collection<E> collection;
//try to invoke default constructor of C
try {
collection = clazz.getDeclaredConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException |
InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException();
}
//Add elements to collection
for (E entry: entries)
collection.add(entry);
return (C) collection;
}
问题是即使double
不能存储在类型Integer
的列表中,以下代码也会运行
//Create and instantiate an ArrayList with element 1.0
ArrayList<Integer> list = initalizeAndAddToCollection(ArrayList.class, 1.0);
System.out.print(list.get(0));
为什么该代码运行,以及如何使之运行,从而导致编译或运行时错误?
编辑:我注意到list.get(0).getClass()
确实会产生异常,但是我不确定为什么会这样(或者为什么以前的代码不会)。
答案 0 :(得分:2)
在Java中,泛型是仅用于编译时的功能,因为它们是通过type erasure实现的。因此,尽管您可能认为自己是通过ArrayList<Integer>
调用在运行时创建newInstance
的,但实际上您实际上只是在创建ArrayList
。
简而言之,反射破坏了Java中泛型的类型安全性。
答案 1 :(得分:1)
此代码在编译时会发出警告。警告说:
Note: MyTest.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
实际上,当您执行此操作时,它会产生4条警告消息:
MyTest.java:5: warning: [unchecked] Possible heap pollution from parameterized vararg type E
initalizeAndAddToCollection(Class<C> clazz, E... entries) {
^
where E,C are type-variables:
E extends Object declared in method <E,C>initalizeAndAddToCollection(Class<C>,E...)
C extends Collection<E> declared in method <E,C>initalizeAndAddToCollection(Class<C>,E...)
MyTest.java:18: warning: [unchecked] unchecked cast
return (C) collection;
^
required: C
found: Collection<E>
where E,C are type-variables:
E extends Object declared in method <E,C>initalizeAndAddToCollection(Class<C>,E...)
C extends Collection<E> declared in method <E,C>initalizeAndAddToCollection(Class<C>,E...)
MyTest.java:23: warning: [unchecked] unchecked method invocation: method initalizeAndAddToCollection in class MyTest is applied to given types
initalizeAndAddToCollection(ArrayList.class, 1.0);
^
required: Class<C>,E[]
found: Class<ArrayList>,double
where C,E are type-variables:
C extends Collection<E> declared in method <E,C>initalizeAndAddToCollection(Class<C>,E...)
E extends Object declared in method <E,C>initalizeAndAddToCollection(Class<C>,E...)
MyTest.java:23: warning: [unchecked] unchecked conversion
initalizeAndAddToCollection(ArrayList.class, 1.0);
^
required: ArrayList<Integer>
found: ArrayList
4 warnings
这些警告说明了为什么编译此看似不正确的代码。您正在执行编译器会告诉您不正确的事情。
为什么此代码运行。
因为它实际上并没有破坏运行时类型安全。
类型擦除意味着该集合实际上正在存储对Object
的引用。
然后执行System.out.print(list.get(0));
时,print
调用的参数类型为Object
。这意味着不需要隐式转换为Integer
。
以及如何使它导致编译或运行时错误?
如果需要编译时错误,请告诉编译器将警告视为错误。 (或检查编译输出中的警告。)
如果需要运行时错误,则可能需要在initalizeAndAddToCollection
方法中添加一些显式的运行时类型检查。