为什么要使用泛型
在明确数据类型的时候可以具体用哪种数据类型,而对于不明确的,则需要泛型来实现
比如
public class TestBean<T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
这里T作为泛型的符号是通用的标记,当然可以任意指定其他的字母作为标记
另一个例子:
TextView textView = new Button(this);
TextView作为Button的父类,所以可以持有子类的引用,那
List<TextView> textViewList = new ArrayList<Button>();
则会报错
,因为 ArrayList<Button>
并不是ArrayList<TextView>
的子类
那这时候就需要引入通配符
通配符
通过 ? extends 定义上界
解决上文中的报错,只需要
List<? extends TextView> textViewList1 = new ArrayList<Button>();
** ? extends ** 表示定义泛型的上界,最高类型不超过extends后的类型
** ? extends TextView ** 表示
List<? extends TextView> textViewList = new ArrayList<Button>();
List<? extends TextView> textViewList1 = new ArrayList<RadioButton>();
List<? extends TextView> textViewList2 = new ArrayList<TextView>();
因为TextView是Button和RadioButton的基类
另外,
List<? extends TextView> textViewList = new ArrayList<Button>();
textViewList.add(new Button(this)); 会报错
TextView textView = textViewList.get(0); 正常编译
上届只可读不可写,因为编译器也不明确知道泛型的类型,所以不可写,但是明确知道上界最高类型,所以可读(个人理解)
通过 ? super 定义下界
明白了上界,其实下界的意思也很容易理解,定义泛型的类型范围不低于super后的类型
比如
List<? super RadioButton> textViewList3 = new ArrayList<View>();
List<? super RadioButton> textViewList4 = new ArrayList<Button>();
List<? super RadioButton> textViewList45 = new ArrayList<TextView>();</pre>
下界只可写不可读
类型擦除
通过如下代码
List<Integer> list1 = new ArrayList<>();
list1.add(123);
List<String> list2 = new ArrayList<>();
list2.add("abc");
System.out.println(list1.getClass() + ";" + list2.getClass()); //输出 class java.util.ArrayList;
class java.util.ArrayListSystem.out.println(list1.getClass() == list2.getClass()); //输出 true
发现List和List的class类型是相同的,因为泛型在编译的时候被编译器给类型擦除了,欺骗虚拟机来达到泛型的目的
另外,类型擦除后的类型会默认为object,那既然类型擦除会默认为object类型,为什么get方法还会将类型转变为Int类型呢,就需要了解类型擦除的原理
类型擦除的原理
通过反编译,得到字节码文件(如何实现反编译得到字节码文件)
public static void main(java.lang.String[]);
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: bipush 123
11: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
14: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
19: pop
20: aload_1
21: iconst_0
22: invokeinterface #6, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
27: checkcast #7 // class java/lang/Integer
30: astore_2
31: return
第27行进行了强转检查,所以即达到了欺骗jvm的目的,又实现了类型的转换
总结
泛型应该作为java的语法糖来看待,而kotlin中的泛型则将这一语法糖更加简化