Java Generics And Collections笔记(Part III)
2018-02-23 15:59 秋风将涌起的某夜 阅读(182) 评论(0) 收藏 举报5 Evolution, not Revolution
泛型代码和legacy代码生成相同的class文件。
skipped
6. Reification
reify翻译为具体化,计算机科学中,reification可理解为run-time类型信息的显式表示。Java中,数组具体化组成元素类型(component type),但是泛型类型不会具体化类型参数信息。
6.1 Reifiable Types
以下类型是可具体化的(reifiable):
- primitive type,如int
- 非参数化的类或者接口,如String, Runnable
- 类型参数是unbounded wildcards的参数化类型,如List<?>
- raw type,如List
- 组成元素类型可具体化的数组,如int[],int[][],List[],List<?>[]
以下类型是不可具体化的:
- 类型变量,如T
- 具有类型实参的参数化类型,如List<String>,Map<String, Integer>
- 有界限(bound)的参数化类型,如Comparable<? super String>, List<? extends Number>
有趣的事,List<? extends Object> 和 List<?> 是等价的,但是前者不可具体化,后者可具体化。
6.2 Instance Tests and Casts
实例的类型检查和类型转换都是在run-time检查类型,也以来依赖reification。
o instanceof Integer;
o instanceof List<E>; //编译错误
参考abstractList的equals实现:
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof List))
return false;
ListIterator<E> e1 = listIterator();
ListIterator<?> e2 = ((List<?>) o).listIterator();
while (e1.hasNext() && e2.hasNext()) {
E o1 = e1.next();
Object o2 = e2.next();
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return !(e1.hasNext() || e2.hasNext());
}
第四行代码使用了raw type的List,推荐使用wildcard type的List<?>,后者可以提供类型保证。
In the most cases, a cast to a type that is not reifiable is flagged with an unchecked warning, whereas an instance test against a type that is not reifiable is always caught as an error.
It is illegal to cast a list of objects to a list of strings, so the cast must take place in two steps. First, cast the list of objects into a list of wildcard type; this cast is safe. Second, cast the list of wildcard type into a list of strings; this cast is permitted but generates an unchecked warning:
6.3 Exception Handling
catch语句会检查抛出的异常是不是给定的类型,和类型检查相似,异常类型必须也是可具体化的。
6.4 Array Creation
数组具体化了组成元素类型,保留了run-time时组成元素的类型,因而可以检测赋值到数组的值是否是允许的,如下:
Integer[] ints = new Integer[] {1, 2, 3};
Number[] nums = ints;
nums[0] = 3.14; // ArrayStoreException
Java中,数组时协变的,第二行代码没问题,但是第三行代码抛出异常,3.14是double类型的,和数组元素的具体化类型不兼容。
也因为类型变量不是可具体化的类型,因此创建泛型数组会出现编译错误,如下:
T[] arr = new T[0];
List<Integer>[] xxs = new List<Integer>[0];
generic arrays are prob- lematic because generics are implemented via erasure, but erasure is beneficial because it eases evolution.
6.5 The Principle of Truth in Advertising
尝试将集合转化为数组,但是创建泛型数组会出错,可以通过unchecked cast强制转换,如下:
public static <T> T[] toArray(Collection<T> c) {
T[] a = (T[])new Object[c.size()];
// ... copy to a
return a;
}
这段代码会有unchecked cast的警告,这个警告也不应该被忽视,如下代码会报错:
List<String> ss = Arrays.asList("one", "two");
String[] a = toArray(ss); // java.lang.ClassCastException
toArray(ss)的返回的数组的组成元素类型是Object,Object[]转化为String[]会失败。
原因就是类型擦除,toArray方法中的类型参数被丢掉,然后用Object代替T,并插入类型转换,如下:
public static Object[] toArray(Collection c) {
Object[] a = (Object[])new Object[c.size()];
// ... copy to a
return a
}
调用会变成如下:
List ss = Arrays.asList("one", "two");
String[] a = (String[])toArray(ss);
the cast that fails may be in a different part of the source code than was responsible for the unchecked warning!
为此,我们要遵循
The Principle of Truth in Advertising: the reified type of an array must be a subtype of the erasure of its static type.
简单的来说,数组的run-time具体化类型必须是声明的类型的子类。可参考这个链接。
创建泛型数组的正确方法是利用反射。参考如下代码,或者jdk中Arrays.copyOf
public static <T> T[] toArray(Collection<T> c, T[] a) {
if (a.length < c.size()) {
a = (T[]) Array.newInstance(a.getClass().getComponentType(), c.size());
}
// ... copy to a
return a;
}
在java.lang.reflect.Array中,newInstance定义如下:
public static Object newInstance(Class<?> componentType, int length)
throws NegativeArraySizeException {
return newArray(componentType, length);
}
返回值类型是Object,而不是Object[],原因是newInstance可能返回int[]等原生类型的数组。
在Collection中,toArray的签名如下
Object[] toArray();
<T> T[] toArray(T[] a);
前者返回的数组的具体化类型是Object,后者和入参数组的具体化类型一致。
同样为了获取数组的具体化类型,函数签名可以如下:
public static <T> T[] toArray(Collection<T> c, Class<T> k)
6.6 The Principle of Indecent Exposure
Although it is an error to create an array with a component type that is not reifiable, it is possible to declare an array with such a type and to perform an unchecked cast to such a type.
a library should never publicly expose an array with a nonreifiable type.
Principle of Indecent Exposure: never publicly expose an array where the components do not have a reifiable type.
参考如下代码:
List<Integer>[] intLists =
(List<Integer>[])new List[] {Arrays.asList(1)}; // unchecked cast
List<? extends Number>[] numLists = intLists;
numLists[0] = Arrays.asList(1.01);
int n = intLists[0].get(0); // class cast exception!
this is a case where an unchecked cast in one part of the program may lead to a class cast error in a completely different part, where the cast does not appear in the source code but is instead introduced by erasure.
浙公网安备 33010602011771号