java泛型(恶心)

一.术语较多

泛型类型 (泛型)                               :List

泛型方法                                         :static List asList(E[] a)

原始类型(原生态类型)(raw type): List 对应的原始类型就是 List

参数化类型                                     :List 

形式类型参数                                 :ListE

实际类型参数(类型参数的实例)      :ListString

type of                                             :<>

通配符                                              :

二.注意

规定:Father是son的父类。

 1.形式类型参数指定为Father,传入子类,自动向上转型。

List list = new ArrayList<>();
list.add(new Son());

   形式参数指定为Son,传入父类,显示强制向下转型。

List list = new ArrayList<>();
list.add((Son) new Father());

2.泛型类型转换为原始类型,编译器对实际类型参数无法检测了。

List before = new ArrayList<>();
before.add(new Father());
        
List after = before;
after.add("hello world");

before添加的是对象,将before赋值给原始类型after后,
检测不到实际类型参数Father的存在,就添加了字符串对象。
这是不安全的操作。

3.List:只能get 不能add

原因:

不知道具体类型参数是什么,也就不知道具体泛型类的内部元素是什么,所以add的值不能保证向下转型成功。

但是上限是Father类型,返回值用Father来接受一定没问题。

4.List     :  只能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 list = new ArrayList();
list.add("hello");

String s = list.get(0);

解答:

1.编写(E)的意义:原来这是编译期时编译器的类型检查,在代码编译时,编译器认为E是真实存在的类型,就好比我们使用时传入的String类型,所以我们需要进行强转,但是当按下运行编译的那一刻,这个E就会被擦出了。

2.如何直接赋值的呢?原来编译器会对代码编译为字节码的时候做手脚,会加上检查类型转换的字节码指令

附加:

就拿上面实际使用的例子生成的字节码:

java泛型(恶心)_第1张图片

可以看到红色框框的checkcast指令,引用了常量表中的String类型。实际对象的类型信息是可以通过对象头中的class指针获取。

 

你可能感兴趣的:(Java,泛型)