一.术语较多
泛型类型 (泛型) :List<E>
泛型方法 :static <E> List<E> asList(E[] a)
原始类型(原生态类型)(raw type): List<E> 对应的原始类型就是 List
参数化类型 :List<String>
形式类型参数 :List<E>的E
实际类型参数(类型参数的实例) :List<String> 的 String
type of :<>
通配符 :?
二.注意
规定:Father是son的父类。
1.形式类型参数指定为Father,传入子类,自动向上转型。
List<Father> list = new ArrayList<>();
list.add(new Son());
形式参数指定为Son,传入父类,显示强制向下转型。
List<Son> list = new ArrayList<>();
list.add((Son) new Father());
2.泛型类型转换为原始类型,编译器对实际类型参数无法检测了。
List<Father> before = new ArrayList<>();
before.add(new Father());
List after = before;
after.add("hello world");
before添加的是对象,将before赋值给原始类型after后,
检测不到实际类型参数Father的存在,就添加了字符串对象。
这是不安全的操作。
3.List<? extends Father>:只能get 不能add
原因:
不知道具体类型参数是什么,也就不知道具体泛型类的内部元素是什么,所以add的值不能保证向下转型成功。
但是上限是Father类型,返回值用Father来接受一定没问题。
4.List<? super Father> : 只能add 不能get
原因:
不知道具体类型参数是什么,但是指定了最低限度是Father类型,也就是实际List内部存储的是Father以及Father超类类型。add的时候有一个要求:只能设置Father 以及 Father的子类。因为一定能保证向上转型。
但是返回值是不能确定的,如果实际返回的是Father的父类对象,拿Father来接收,会抛出异常。
三.疑惑
看Arraylist源码看到一处代码:
E elementData(int index) {
return (E) elementData[index];
}
上面类型擦除之后不就变成:
Object elementData(int index) {
return (Object) elementData[index];
}
那强转有什么意义呢?而且,在下面我们实际使用的时候,是怎么直接可以赋值给String变量的呢?
实际使用:
ArrayList<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0);
解答:
1.编写(E)的意义:原来这是编译期时编译器的类型检查,在代码编译时,编译器认为E是真实存在的类型,就好比我们使用时传入的String类型,所以我们需要进行强转,但是当按下运行编译的那一刻,这个E就会被擦出了。
2.如何直接赋值的呢?原来编译器会对代码编译为字节码的时候做手脚,会加上检查类型转换的字节码指令。
附加:
就拿上面实际使用的例子生成的字节码:
可以看到红色框框的checkcast指令,引用了常量表中的String类型。实际对象的类型信息是可以通过对象头中的class指针获取。