在java泛型出现以前,程序员们普遍杂乱地使用Object变量,然后进行强制类型转换,泛型的出现带来了更好的安全性和可读性。泛型对于结合类尤其游泳,就比如我们经常使用的ArrayList。
泛型定义
public class Pair
{
private T first;
private T second;
public Pair() { first = null; second = null; }
public Pair(T first, T second) { first = first; second = second; }
public T getFirst() { return first; }
public T getSecond() { return second; }
public T setFirst(T newValue) { first = newValue; }
public T setSecond(T newValue) { second = newValue; }
}
T:表示任意类型
实例化泛型类型:
Pair
构造函数:
Pair
Pair
返回泛型方法:
public static Pair
{
…
retrun Pair<>(min, max);
}
泛型方法
class ArrayAlg
{
public static
{
return a[a.length/2];
}
}
泛型方法可以放在普通类中,也可以放在泛型类中。
调用:
String middle = ArrayAlg.
当编译器有足够信息能推断出T的类型时,上述调用也可以省略为:
String middle = ArrayAlg.getMiddle(“john”,”Q”,”public”);
大多数情况下这样调用是没有问题的,但是也有例外
double middle = ArrayAlg.getMiddle(3.14,1729,0);
编译器在推断时发现前三个参数类型为Double和两个Integer,然后寻找这三个参数的共同类型,发现有两个父类型:Number和Comparable接口,这种情况下,可以把三个参数都写为double格式来避免。
类型变量的限定
public static
{
if (a == null || a.length == 0) return null;
T min = a[0];
T max = a[0];
for(int i=1;i<a.length;i++)
{
if(min.compareTo(a[i]) > 0)
min = a[i];
if(min.compareTo(a[i]) < 0)
max = a[i];
}
return new Pair<>(min,max);
}
如上述代码,如果你需要的泛型接口必须拥有compareTo方法,那么就需要加上extends Comparable限定。
泛型代码和虚拟机
虚拟机中没有泛型,只有普通的类和方法
所有的类型参数都用他们的限定类型替换
桥方法被合成来保持多态
为保持类型安全性,必要时插入强制类型转换
因此在虚拟机处理时,会擦除类型变量,上述Pair类型会被擦除为:
public class Pair
{
private Object first;
private Object second;
public Pair() { first = null; second = null; }
public Pair(Object first, Object second) { first = first; second = second; }
public Object getFirst() { return first; }
public Object getSecond() { return second; }
public Object setFirst(Object newValue) { first = newValue; }
public Object setSecond(Object newValue) { second = newValue; }
}
因为T没有限定,所以直接被擦除为Object,如果有限定的类型话就用第一个限定类型来替换,如
public class Interval
{
private T lower;
private T upper;
…
public Interval(T first,T second){…}
}
擦除后会变为:
public class Interval implements Serializable
{
private Comparable lower;
private Comparable upper;
…
public Interval(Comparable first,Comparable second){…}
}
#翻译泛型表达式
当程序调用泛型方法时,如果擦除返回类型,编译器插入强制类型转换,如下语句
Pair
Employee buddy = buddies.getFirst();
getFirst被擦除后将返回Object类型,编译器自动插入Employee的强制类型转换,也就是说,编译器会把这个方法调用翻译为两条虚拟机指令
对原始方法Pair.getFirst的调用
将返回的Object类型强制转换为Employee类型。
#翻译泛型方法
类型擦除也会出现在泛型方法中,如
public static
擦除类型后,只剩下一个方法
public static Comparable min (Comparable[] a)
这样带来了两个复杂问题,如下示例
class DateInterval extends Pair
{
public void setSecond(Date second)
{
if(second.compareTo(getFirst() >= 0))
super.setSecond(second);
}
}
一个日期区间DateInterval是一对Date对象,而且限定second的值不能小于第一个值,这个类擦除类型后变为
class DateInterval extends Pair //after erasure
{
public void setSecond(Date second)
{
if(second.compareTo(getFirst() >= 0))
super.setSecond(second);
}
}
令人奇怪的是,存在另一个从Pair继承而来的setSecond方法,即
public void setSecond(Object second)
从重载的角度看,这显然是另一个方法,因为参数不同,但这样会带来一些问题。考虑下列语句序列:
DateInterval interval = new DateInterval(…);
Pair
pair.setSecond(aDate);
从多台的动态绑定考虑,我们肯定希望setSecond调用的是子类DateInterval.setSecond,问题在于类型擦除与多态放生了冲突,虚拟机中不存在泛型类型,因此会直接调用Pair.setSecond(Object …)。
要解决这个问题,就需要编译器在DateInterval类中生成一个桥方法(编译器自动生成)
public void setSecond(Object second) { setSecond((Date) second)}
https://www.cnblogs.com/xxzhuang/p/5968331.html
约束与局限性
不能用基本类型实例化类型参数
其本职是因为在类型擦除后,无法用Object来存储基本类型
运行时类型查询只适用于原始类型
if(a instanceof Pair
实际上仅仅测试a是否是任意类型的一个Pair
或强制类型转换:
Pair
以上都会触发编译告警
同理getClass方法总是会返回原始类型,例如
Pair
Pair
if (stringPair.getClass() == employeePair.getClass()) //they are equal
比较结果都是true,因为都返回Pair.class