J1.泛型方法
泛型方法的规定
- 泛型方法可以放在普通类内也可以放在泛型类内
- 基本格式为
public static <T> T name(){}
类型变量:就是<T>,他放在修饰符的后面(public static)
T为返回值类型,他放在类型变量后面
name就是该泛型方法的名字了! - 调用泛型方法时可以不填类型变量,JAVA可以自动根据形参推断(但最好填上)
基本泛型方法
以下代码实现找到一个数组内中间的那一个元素(下面包含对对应元素的解释)
- 解释:定义了一个静态的,接收一可变长数组且返回T类型数值的函数
- T… a 表示一个可变长的数组,所以a就是一个数组!
- return 一个T类型的数据,这里取数组
public static <T> T getMiddle(T... a){
return a[a.length/2];
}
调用该泛型方法(假定该泛型方法放在一名为 Tmethod的普通类中)
String middle = Tmethod.<String>getMiddle("hello","world","nood","python");
System.out.println(middle);
// 返回nood
类型变量的限定
代码实现找到数组中最小值,方法就是逐个比较
因为我们需要在比较中使用compareTo方法,但该方法仅限于继承了comparable接口的类使用所以我们为类型变量进行限定:
<T extends Comparable>
将调用者传入的类型限制为继承了comparable接口的类(限定的可以是类或者接口)
public static <T extends Comparable> T min(T[] a) {
if (a == null || a.length == 0) return null;
T smallest = a[0];
for(int i=0;i<a.length;i++){
if(smallest.compareTo(a[i])>0) smallest=a[i];
}
return smallest;
}
一个类型变量可以使用多个限定,多个限定之间使用 & 隔开,如
<T extends xxx & xxx>
J2.泛型代码和虚拟机
类型擦除
- JAVA泛型不同于C++的泛型,可以把JAVA泛型视为“使用了泛型擦除的伪泛型”
- 如下方代码,我们继承了comparable接口,该接口就作为上界,而类型变量T相当于下界
- 当我们使用Pair<String>时,确定了下界为String,而上界就是comparable,JAVA在处理时,会把下界擦除并转而使用上界,这就是所谓的泛型擦除(也就是说,我们实际上使用的是Pair<Comparable>)
- 上界也叫作原始类型
Pair<T extends Comparable>
Pair<String>
注意!当我们继承多个限定类型后,原始类型就被确定为第一个限定类型,所以以下代码经过类型擦除后,原始类型使用的是Serializable而不是Comparable
Pair<T extends Serializable & Comparable>
翻译泛型
首先定义泛型类Pair,以下所有翻译泛型解释都基于该类
class Pair<T> {
private final T first;
private final T second;
public Pair(T first, T second) {
this.first = first;
this.second = second;
}
public T getFirst() {
return first;
}
public T getSecond() {
return second;
}
}
翻译泛型表达式
代码中我们使用类Pair,并且指定其类型参数为String
- 规定,如果不写方法的返回值类型,那么编译器插入强制类型转换
- 解析:原本p.getFirst()理应写成p.<String>getFirst(),来表示其返回一个字符串类型,但我们这里略去不写,JAVA就会先按照类型擦除原则返回一个Object类型的值,然后立即进行强制类型转换,把他变回String类型的值,最后完成赋值操作
Pair<String> p = new Pair<>("hey","not");
String s = p.getFirst();
翻译泛型方法
泛型转换的四个事实
- 虚拟机中没有泛型,只有普通的类和方法
- 所有的类型参数都用他们的限定类型替换
- 桥方法被合成来保持多态
- 为了保持类型的安全性,必要时插入强制类型转换
J3.约束和局限性
不能使用基本类型实例化类型参数
简言之,就是不可以用double来实例化类型参数,但是可以使用Double!
当包装器类型不接受替换时,可以使用独立的方法或者类处理他们
运行时查询只适合原始类型
不要试图使用instanceof来检测泛型类型,如下代码所示必定返回一个错误
Pair<String> p;
if(p instanceof Pair<String>)
泛型强制类型转换会导致警告
Pair<String> s = (Pair<String>) p;
对一个泛型对象使用getClass()返回原始类型