泛型
1、泛型的好处
在泛型未产生之前,对于不确定的类型通常使用Object类型来任意化转换,这就要求代码编写者对于未知类的类型需要预知,而强制类型转换出错并不会在编译时报错,而是在真正运行时才出错。而泛型类则可以隐式的自动转换类型,且无需预知类型,提供了安全的类型转换策略。
2、泛型擦除
泛型擦除的意思是对于编译的时候,泛型的类型会被抹除掉,即不论泛型指定任何类型最终都会变为原始类型,即ArrayList、List、Map等。
List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
System.out.println(list1.getClass() == list2.getClass());//true
而泛型对类型的检查在编译之前就会检查,如果类型不匹配则无法进行编译
3、泛型的通配符
1)、基本类型:其实本质上没有区别,只是习惯性的使用。
- T:一个具体的Java类型
- E:集合中的元素类型
- K、V:Map中的键和值
- ?:表示不确定的Java类型
2)、泛型通配符
-
?:无界通配符,由于通配符的类型不确定,故无法进行多重限定,而T类型则可以,使用&进行限定间的连接
-
? extends E:上界通配符,此时的泛型范围在E或者E的子类均可,且可以直接调用E的方法,无需进行强制类型转换。存在多个类型需要界定时采用逗号分开。
class Animals { } class Dogs extends Animals{ } //通配符对于作为方法的参数时,可以起到范围类型限定的作用 private int getNumber(List<? extends Animals> animals) { return animals.size(); } //只允许一个类型的参数进行传递 private int getNumber2(List<Animals> animals) { return animals.size(); } public void test() { List<Dogs> dogs = new ArrayList<>(); getNumber(dogs); getNumber2(dogs);//编译错误 }
-
?super E:下界通配符,类似与上界通配符,不管范围相反,用来界定E类型,或者E类的父类型,直到Object
一般来说,无界用来处理任何信息都不知道的类型,而上界通配符用于写操作,写入的是已知类或其子类,下界通配符则用来读操作,读取已知类及其父类。
4、调用泛型方法
调用泛型方法的方式有两种,一种是指定泛型,一种是不指定泛型,二者在执行的时候会存在一些差别
-
指定泛型:指定泛型之后,如果使用其他类型的参数,则会无法通过编译
String s = TestFan.<String>get("a", "b"); TestFan.<String>get(1, 1.2);//编译出错
-
不指定泛型:对于不指定泛型,则会根据两个参数的最小共同父类作为返回值类型,此时无论输入任何类型均可以正常编译
//不指定泛型类型 Integer integer = get(1, 2); String s = get("a", "cd"); Number number = get(1, 1.1); Double aDouble = get(1.2, 2.3);
5、?与 T的区别
- ?是无界通配符,类型不确定,而T则是一个确定的类型,只表示一个类型
- ?不可有多重界定,只有一个上界或下届,而T是一个确定的类型,可以有多重界定,但是不存在下界
- T类型一般用于保证泛型一致,而?则无法保证
- T类型可以操作,即作为类型来定义变量,而?无法操作
- 用作Class<?>时,可以正常使用,但是Class的使用必须让T类型有结果,即必须是实际类型或者本类变为T类型的泛型类