1:什么是泛型?
泛型的本质是参数化类型的应用,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。而这个参数必须是引用类型。其中:
ArrayList<T>中的T称为类型参数变量
ArrayList<Integer>中的Integer称为实际类型参数
整个ArrayList<T>称为泛型类型
整个ArrayList<Integer>称为参数化的类型(ParameterizedType)
2 : 引入泛型的好处?
2.1 类型安全
泛型的主要目标是提高 Java 程序的类型安全
编译时期就可以检查出因 Java 类型不正确导致的 ClassCastException 异常
符合越早出错代价越小原则
2.2 消除强制类型转换
使用时直接得到目标类型,消除许多强制类型转换,使得代码更加可读,并且减少了出错机会
2.3 潜在的性能收益
由于泛型的实现方式,支持泛型(几乎)不需要 JVM 或类文件更改
所有工作都在编译器中完成
编译器生成的代码跟不使用泛型(和强制类型转换)时所写的代码几乎一致,只是更能确保类型安全而已
3:泛型的声明方式
3.1 泛型类的声明
public class GenericClass <T> { };
3.2 泛型接口的声明
interface Generic <T> { };
3.3 泛型方法的声明
<T > void get (T data) () { };
4 : 用泛型实现元组
元组其实是关系数据库中的一个学术名词,一条记录就是一个元组,一个表就是一个关系,纪录组成表,元组生成关系,这就是关系数据库的核心理念,在 Java 中我们如果想优雅实现元组就可以借助泛型类实现,如下是一个三元组类型的实现:
5:类型擦除
5.1 为什么 Java 泛型要通过擦除来实现?擦除有什么坏处或者说代价?
可以说 Java 泛型的存在就是一个不得已的妥协,正因为这种妥协导致了 Java 泛型的混乱,甚至说是 JDK 泛型设计的失败。Java 之所以要通过擦除来实现泛型机制其实是为了兼容性考虑,只有这样才能让非泛化代码到泛化代码的转变过程建立在不破坏现有类库的实现上。正是因为这种兼容也带来了一些代价,譬如泛型不能显式地引用运行时类型的操作之中(如向上向下转型、instanceof 操作等),因为所有关于参数的信息都丢失了,所以任何时候使用泛型都要提醒自己背后的真实擦除类型到底是什么;此外擦除和兼容性导致了使用泛型并不是强制的(如 List list = new ArrayList(); 等写法);其次擦除会导致我们在编写代码时十分谨慎(如不想被擦除为 Object 类型时不要忘了添加上边界操作等)。
如下声明都是错误的,因为泛型擦除丢失了在泛型代码中执行某些操作的能力,任何在运行时需要知道确切类型信息的操作都将无法工作
private void func(Object arg){
T [] aTs=new T[3];//数组中不能使用泛型
T var=new T();//实例时需要知道确切的类型信息
if(arg instanceof T){};//因为在 Java 编译期没法确定泛型参数化类型,也就找不到对应的类字节码文件,所以自然就不行了,此外由于 T 被擦除为 Object,如果可以 new T() 则就变成了 new Object(),失去了本意
}
5.2 案例分析
6:泛型中的通配符
泛型中的通配符分为如下三种:
<?>无限制通配符
<? extends T> extends 关键字声明了类型的上界,表示参数化的类型可能是所指定的类型,或者是此类型的子类
<? super T> super 关键字声明了类型的下界,表示参数化的类型可能是指定的类型,或者是此类型的父类
注意下面会报错,因为 Java 类型参数限定只有 extends 形式,没有 super 形式
Class Bean<T super Number>
网友评论