只标记重要的部分
什么是泛型?
泛型即参数化类型,参数化类型就是说将类型由原来具体给定的某一个类型参数化,也就是说现在的类型是一个参数,由这个参数决定到底是什么类型。
JavaSE7及以后,构造函数中可以省略泛型类型:
ArrayList<String> list=new ArrayList<>();
类型变量放在修饰符的后面,返回类型的前面。
class ArrayAlg
{
public static <T> T getMiddle(T...a)
{
return a[a.length/2];
}
}
类型变量的限定
public static <T extends Comparable> T min(T[] a)
表示T是Comparable类型的子类型,一个类型变量或通配符可以有多个限定
限定类型用&分隔,逗号用来分隔类型变量
可以有多个接口超类型,但是限定中至多有一个类。
如果用一个类作为限定,它必须是限定列表中的第一个。
泛型代码和虚拟机
虚拟机没有泛型类型对象---所有对象都属于普通类
在虚拟机中,无论何时定义一个泛型类型,都自动提供了一个相应的原始类型,原始类型的名字就是删去类型参数后的泛型类类名。
Pair<T>的原始类型为Pair
Java泛型转换的事实:
- 虚拟机中没有泛型,只有普通的类和方法。
- 所有的类型参数都用他们的限定类型替换
- 桥方法被合成来保持多态
- 为保持类型安全性,必要时插入强制类型转换
使用泛型类时,虽然传入了不同的泛型实参,但并没有真正意义上生成不同的类型,传入不同泛型实参的泛型类在内存上只有一个,即还是原来最基本的类型,举个栗子也就是Pair<T>的原始类型为Pair,原因在于java泛型这一概念提出的目的,导致其只是作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息擦除,也就是说成功编译过后的class文件是不包含任何泛型信息的,泛型信息不会进入到运行阶段,不会进入虚拟机运行阶段。
- 对此总结成一句话,泛型类型在逻辑上可以看成是多个不同的类型,实际上都是相同的基本类型。
约束与局限性
- 不能用基本类型实例化类型参数,因此没有Pair<double>,只有Pair<Double>
- 虚拟机中的对象总有一个特定的非泛型类型。因此所有的类型查询只产生原始类型。
- 试图查询一个对象是否属于某个泛型类型时,倘若使用instanceof会得到一个编译器错误,如果使用强制类型转换会得到一个警告。
- 不能创建参数化类型的数组
- 泛型类的静态上下文中类型变量无效
public class Singleton<T>
{
private static T singleInstance();
}
上述是无效的
- 既不能抛出也不能捕获泛型类对象,甚至泛型类扩展Throwable都是不合法的
public class Problem<T> extends Exception
- 一个类或者类型变量不能同时成为两个接口类型的子类,而这两个接口是同一接口的不同参数化。如下
class Employee implements Comparable<Employee>{}
class Manager extends Employee implements Comparable<Manager>{}
Manager会实现Comparable<Employee>和Comparable<Manager>,这是同一接口的不同参数化。
- 带有超类型限定的通配符可以向泛型对象写入,带有子类型限定的通配符可以从泛型对象读取。
- 通配符不是类型变量,因此不能在代码中使用“?”作为一种类型
- 此处’?’是类型实参,而不是类型形参 !再直白点的意思就是,此处的?和Number、String、Integer一样都是一种实际的类型,可以把?看成所有类型的父类。是一种真实的类型。
在逻辑上Box<Number>不能视为Box<Integer>的父类。因为在编译阶段以后会进行类型擦除,都成了Box类
-
只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
- 这才是一个真正的泛型方法。
- 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
- 这个T可以出现在这个泛型方法的任意位置.
- 泛型的数量也可以为任意多个
- 如:public <T,K> K showKeyName(Generic<T> container){}
类中的泛型方法
//在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型。可以类型与T相同,也可以不同。
//由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型,编译器也能够正确识别泛型方法中识别的泛型。
public <E> void show_3(E t){
System.out.println(t.toString());
}
在泛型类中声明了一个泛型方法,使用泛型T,注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。
public <T> void show_2(T t){
System.out.println(t.toString());
}//在泛型方法中添加上下边界限制的时候,必须在权限声明与返回值之间的<T>上添加上下边界,即在泛型声明的时候添加
//public <T> T showKeyName(Generic<T extends Number> container),编译器会报错:"Unexpected bound"下面的声明则不会报错
public <T extends Number> T showKeyName(Generic<T> container){