泛型程序设计:意味着编写的代码可以被很多不同类型的对象所重用。
类型参数:ArrayList<String> files = new ArrayList<>()
(String便是类型参数,且构造器中可省去,省去的类型依据变量的类型推断) 泛型类: 具有一个或多个类型变量的类。
public class Pair<T>
{
private T first;
private T second;
public Pair() {first = null;second = null ;}
public Pair(T first,T second) {this.first = first;this.second = second;}
public T getFirst() {return first;}
public T getSecond() {return second ;}
public void setFirst(T newValue) {first = newValue;}
public void setSecond(T newValue) {second = newValue;}
}
//可以有多个类型变量 :public calss Pair<T,U>{...}(通常事大写的形式)
复制代码
泛型方法: 类型变量放在修饰符的后面,返回类型的前面(该方法既可以放在普通类,也可以放在泛型类)
class ArrayAlg
{
public static <T> T getMiddle(T... a)
{
return a[a.length/2];
}
}
复制代码
泛型方法的调用:
String middle = ArrayAlg.<String>getMiddle("John","A","Public");
- 大多数情况下可省去
<String>
类型参数(编译器可推断);
类型变量限定:保证使用该类型对象的时候调用的方法存在
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;
}
复制代码
- 在类型变量中的
extends
表示的是T是绑定类型的子类型,T可以绑定的类型既可以是类,也可以是接口。 - 绑定多个限定:
T extends Comparable & Serializable
(限定类型用‘&’分隔,‘,’分隔类型变量)
泛型代码和虚拟机:
- 类型擦除:定义泛型类型的同时提供了相应的原始类型(原始类型的名字便是删去类型参数后的泛型类型名),擦除类型变量,并替换为限定类型(无限定类型的用Object),结果是一个普通的类。
public class Pair
{
private Object first;
private Object second;
public Pair() {first = null;second = null ;}
public Pair(Object first,Object second) {this.first = first;this.second = second;}
public Object getFirst() {return first;}
public Object getSecond() {return second ;}
public void setFirst(Object newValue) {first = newValue;}
public void setSecond(Object newValue) {second = newValue;}
}
复制代码
- 有限定类型的情况下,选第一个作为原始类型的替换
public class Interval<T extends Comparable & Serializable > implements Serializable
{
private T lower;
private T upper;
...
public Interval(T first,Ts econd)
{
if(first.compareTo(second)<=0){lower=first;upper=second;}
else{lower=second;upper=first;}
}
}
//原始类型
public class Interval implements Serializable
{
private Comparable lower;
private Coiparable upper;
....
public Interval(Coiparable first,Coiparable second){...}
}
复制代码
为了提高效率,最好将标签接口(没有方法的接口)放在边界末尾 翻译泛型表达式:调用泛型方法,若擦除返回类型,编译器插入强制类型转换 桥方法:为了解决多态与类型擦除的冲突 Java泛型转换的事实:
- 虚拟机中没有泛型,只有普通的类和方法
- 所有的类型参数都用它们的限定类型替换。
- 桥方法被合成来保持多态
- 为保持类型安全性,必要时插入强制类型转换
约束与局限性:
- 不能用类型参数代替基本类型;类似的 没有
Pair<double>
,只有Pair<Double>
- 运行时类型查询只适用于原始类型,(虚拟机中总有一个特定的非泛型类型,So所有的类型查询只产生原始类型)
- 不能创建参数化类型的数组(记住,是不能创建
Pair<String>[] T = new Pair<String>[10];
,声明变量Pair<String>[] T
还是可以的, 原因:对于泛型类型,即使通过了数组存储检查,还是导致类型错误。 - Varargs警告(向参数个数可变的方法传递一个泛型类型的实例,也就是说参数可变时,实际创建的时一个参数数组,这时创建的时泛型类型的数组,在这种情况下,不会报错,而是含警告)
- 不能实例化类型变量,例如
publicPair(){first=newT();second=newT();}//Error
(类型擦除将T改变成Object,解决方法: javaSE之后可以让调用者提供构造器表达式,传统的办法是使用Class.newInstance
构造泛型对象) - 不能构造泛型数组
- 泛型类的静态上下文中类型变量无效(不能在静态域或方法中引用类型变量)
- 不能抛出或捕获泛型类的实例(在异常规范中使用变量是允许的)
- 可以消除对受查异常的检查(异常处理回忆:必须为所有受查异常提供一个处理器)
- 注意擦除后的冲突(要想支持擦除的转换,就需要强行限制一个类或类型变量不能同时成为两个接口类型的子类,而这两个是同一个接口的不同参数化)
泛型类型的继承规则
- 举个例子,
Pair<T>
与Pair<S>
是没有关系的 - 永远可以将参数化类型转换为一个原始类型
- 泛型类可以扩展或实现其他的泛型类
通配符类型:
- 通配符类型中,允许类型参数变化。(上边界限定通配符,安全的读泛型对象读取(如接受返回值))通配符类型:
Pair<?extends Employee〉
-
- (下边界限定通配符,向泛型对象写入(如传递参数)通配符的超类型限定:
? super Manager
,这个通配符限制为Manager的所有的超类型 - 它的行为:.可以为方法提供参数,但不能使用返回值
? super Manager getFirst() void setFirst(? super Manager)
(先看第一个返回的,由于返回的类型是Manager的超类,所以Object类呀Employee呀都有可能,那么你用什么类型的引用去接收这个返回值?编译器无法知道getFirst返回值的真正类型,只有通过Object引用去接收才是合法的。再看第二个setFirst参数中的通配符,编译器同样无法知道参数的具体类型,但是这个类型是有“下界”的,就是说传来的参数只要是Manager类或者它的子类,那么这样的传参都是可以接受的,编译器由于知道下界,这些传参就是安全的。)- 无限定通配符:
Pair<?>
- (下边界限定通配符,向泛型对象写入(如传递参数)通配符的超类型限定: