声明:本文章内容主要摘选自尚硅谷宋红康Java教程、《Java核心卷一》、廖雪峰Java教程,示例代码部分出自本人,更多详细内容推荐直接观看以上教程及书籍,若有错误之处请指出,欢迎交流。
一、擦拭法
泛型是一种类似“模板代码”的技术,不同语言的泛型实现方式不一定相同。Java语言的泛型实现方式是擦拭法(Type Erasure)。
所谓擦拭法是指,虚拟机对泛型其实一无所知,所有的工作都是编译器做的。
//例如,我们编写了一个泛型类Pair<T>,这是编译器看到的代码:
public class Pair<T> {
private T first;
private T last;
public Pair(T first, T last) {
this.first = first;
this.last = last;
}
public T getFirst() {
return first;
}
public T getLast() {
return last;
}
}
//而虚拟机根本不知道泛型。这是虚拟机执行的代码:
public class Pair {
private Object first;
private Object last;
public Pair(Object first, Object last) {
this.first = first;
this.last = last;
}
public Object getFirst() {
return first;
}
public Object getLast() {
return last;
}
}
因此,Java使用擦拭法实现泛型,导致了:
- 编译器把类型<T>视为Object;
- 编译器根据<T>实现安全的强制转型。
//使用泛型的时候,我们编写的代码也是编译器看到的代码:
Pair<String> p = new Pair<>("Hello", "world");
String first = p.getFirst();
String last = p.getLast();
//而虚拟机执行的代码并没有泛型:
Pair p = new Pair("Hello", "world");
String first = (String) p.getFirst();
String last = (String) p.getLast();
所以,Java的泛型是由编译器在编译时实行的,编译器内部永远把所有类型T视为Object处理,但是,在需要转型的时候,编译器会根据T的类型自动为我们实行安全地强制转型。
二、泛型的约束与局限性
了解了Java泛型的实现方式——擦拭法,我们就知道了Java泛型的局限,大多数限制都是由类型擦除引起的
-
1.<T>不能是基本类型,例如int,因为实际类型是Object,Object类型无法持有基本类型
Pair<int> p = new Pair<>(1, 2); // compile error!
-
2.无法取得带泛型的Class
Pair<string>p1=new Pair<>("Hello","world");
Pair<Integer>p2=new Pair<>(123,456);
Class c1=p1.getClass();
Class c2=p2.getClass();
System.out.println(c1==c2);//true
System.out.println(c1==Pair.class);//true
/*因为T是Object,我们对Pair<String>和Pair<Integer>类型获取Class时,获取到的是同一个Class,也就是Pair类的Class。
换句话说,所有泛型实例,无论T的类型是什么,getClass()返回同一个Class实例,因为编译后它们全部都是Pair<Object>。
*/
- 3.无法判断带泛型的Class
Pair<Integer> p = new Pair<>(123, 456);
// Compile error:
if (p instanceof Pair<String>.class) {
}
//原因和前面一样,并不存在Pair<String>.class,而是只有唯一的Pair.class。
- 4.不能创建参数化类型的数组
Pair<Integer>[] table = new Pair<>[10];//Error
/*需要说明的是,只是不允许创建这些数组,而声明类型为Pair<String>[]的变量仍是合法的。不过不能用new Pair<String>[10]初始化这个变量。
这有什么问题呢?擦除之后,table的类型是Pair[]。可以把它转换为Object[]: Object[] objarray = table;
数组会记住它的元素类型,如果试图存储其他类型的元素,就会抛出一个Array-StoreException异常: */
objarray[0]="Hel1o";//Error--component type is Pair
//不过对于泛型类型,擦除会使这种机制无效。以下赋值:
objarray[0]=new Pair<Employee>();
//能够通过数组存储检查,不过仍会导致一个类型错误。出于这个原因,不允许创建参数化类型的数组。
提示:如果需要收集参数化类型对象,只有一种安全而有效的方法:使用ArrayList: ArrayList<Pair
>。
- 5.不能实例化T类型
public class Pair<T> {
private T first;
private T last;
public Pair() {
// Compile error:
first = new T();
last = new T();
}
}
//上述代码无法通过编译,因为构造方法的两行语句:
first = new T();
last = new T();
//擦拭后实际上变成了:
first = new Object();
last = new Object();
//这样一来,创建new Pair<String>()和创建new Pair<Integer>()就全部成了Object,显然编译器要阻止这种类型不对的代码。
//要实例化T类型,我们必须借助额外的Class<T>参数:
public class Pair<T> {
private T first;
private T last;
public Pair(Class<T> clazz) {
first = clazz.newInstance();
last = clazz.newInstance();
}
}
//上述代码借助Class<T>参数并通过反射来实例化T类型,使用的时候,也必须传入Class<T>。例如:
Pair<String> pair = new Pair<>(String.class);
- 6.不能在静态域或方法中引用类型变量
public class Singleton<T>
{
private static T singlelnstance;//Error
public static T getSingleInstanceO//Error
{
if(singleInstance ==null)
return singleInstance;
}
}
/*如果这个程序能够运行,就可以声明一个Singleton<Random>共享随机数生成器,声明一个Singleton<JFileChooser>共享文件选择器对话框。
但是,这个程序无法工作。类型擦除之后,只剩下Singleton类,它只包含一个singlelnstance域。因此,禁止使用带有类型变量的静态域和方法。*/
- 7.不能抛出或捕获泛型类的实例
既不能抛出也不能捕获泛型类对象。实际上,甚至泛型类扩展Throwable都是不合法的。
例如,以下定义就不能正常编译:
public static <T extends Throwable> void dolork(Class<T>t)
{
try
{
...
}catch(T e)//Error--can't catch type variable
{
Logger.global.info(.…)
}
}
//不过,在异常规范中使用类型变量是允许的。以下方法是合法的:
public static <T extends Throwable> void doWork(T t)throws T//0K
{
try
{
...
}catch(Throwable realCause)
{
t.nitCause(realCause);
throw t;
}
}
三、不恰当的覆写方法
有些时候,一个看似正确定义的方法会无法通过编译。例如:
public class Pair<T> {
public boolean equals(T t) {
return this == t;
}
}
这是因为,定义的equals(T t)方法实际上会被擦拭成equals(Object t),而这个方法是继承自Object的,编译器会阻止一个实际上会变成覆写的泛型方法定义。
换个方法名,避开与Object.equals(Object)的冲突就可以成功编译:
public class Pair<T> {
public boolean same(T t) {
return this == t;
}
}
此笔记仅针对有一定编程基础的同学,且本人只记录比较重要的知识点,若想要入门Java可以先行观看相关教程或书籍后再阅读此笔记。
最后附一下相关链接:
Java在线API中文手册
Java platform se8下载
尚硅谷Java教学视频
《Java核心卷一》