泛型是指参数化类型的能力,定义带泛型类型的类或方法,随后编译器会使用具体类型来替换他
主要优点是在编译期而不是运行时检测出错误
泛型类和接口
public class Stack<T> {
private List<T> list = new ArrayList<T>();
public void push(T o) {
list.add(o);
}
public T pop() {
T o = list.get(list.size() - 1);
list.remove(list.size() - 1);
return o;
}
}
泛型方法
public static <T> void print()
可以将泛型指定为另一种类型的子类型,被称为受限的
public static <T extends GeometricObject> boolean equalArea(T object1, T object2) {
reutrn object1.getArea() == object2.getArea();
}
原始类型和向后兼容
可以使用泛型类而不指定具体类型,称为原始类型
,这是不安全的
Stack stack = new Stack();
通配泛型
public static void main(String[] args) {
Stack<Integer> stack = new Stack<Integer>();
max(stack);
}
public static double max(Stack<Number> stack) {
...
}
尽管Integer
是Number
的子类型,但Stack<Integer>
不是Stack<Number>
的子类型,为避免这个问题,可以使用通配泛型类型
有三种形式:
?
非受限通配,和? extends Object
是一样的? extends T
受限通配,表示T或T的一个未知子类型? super T
下限通配,表示T或T的一个未知父类型
使用下面的语句可以修复上面的错误:
public static double max(Stack<? extends Number> stack) {
...
}
消除泛型和对泛型的限制
泛型使用一种称为类型消除
的方法来实现,编译器使用泛型类型信息来编译代码,泛型存在于编译时,一旦编译器确认泛型类型是安全使用的,随后会翻译成原始类型
不管实际的具体类型是什么,泛型类是被它所有实例共享的
ArrayList<String> list1 = new ArrayList<String>();
ArrayList<Integer> list2 = new ArrayList<Integer>();
编译时是两种类型,但是运行时只有一个ArrayList
类被加载到JVM中,list1和list2都是ArrayList
的实例
泛型在运行时会被消除,所以使用上是有限制的:
- 不能使用
new T()
,运行时泛型类型不可用 不能使用
new T[]
T[] arrays = new T[10];
可以使用类型转换方法来规避限制
T[] arrays = (T[]) new Object[10];
不能使用泛型类创建泛型数组
ArrayList<String>[] list = new ArrayList<String>[10];
可以使用类型转换方法来规避限制
ArrayList<String>[] list = (ArrayList<String[]) new ArrayList[10]
2和3都将会的一个编译警告
在静态环境下不允许类的参数是泛型类型
public class Test<T> { public static void m(T o1) { } public static T o1; static { T o2; } }
异常类型不能是泛型的
基本类型无法做为类型参数,但是有自动打包和自动拆包的功能,可以方便的在基本类型和对应包装类型之间转换
可以在类中包含泛型方法,而这个类可以是泛型类,也可以不是泛型类,尽量只使用泛型方法,而不是将整个类泛型化
对于static方法而言,无法访问泛型类的类型参数,如果static方法需要使用泛型能力,必须使其成为泛型方法
JAVA的泛型和其他语言比起来很弱