参考Java核心第一卷 基础知识
1.泛型解决的主要问题
(1)可以被重用;
(2)更好的可读性和安全性
2.泛型程序设计使用
(1)仅仅使用泛型类,比如 List<String> list = new ArrayList<String>
(2)简单泛型类定义,比如 class SortedUtil<T> { }
(3)泛型类型限定和通配符:extends 子类型限定,super 超类型限定,比如
/**
* 定义泛型类 SortedUtl<T>,* 注意类型参数 T
* (1)可以指定子类限定类型:比如 extends A&B&C 这里面 A,B,C代表多类或者接口
* 但是根据 extends 关键字我们知道:只能继承自一个类,因此 A、B、C...中只能有一个类
* 并且如果确实有这么复杂的限定类型,那么这个类还必须放在第一位置!
*
* (2)还可以使用 super 指定超类限定类型
*
* (3)也可使用 ? 通配符
*
* @param <T>
*/
class SortedUtl<T extends Comparable<? super User> & Serializable> {
}
3.泛型擦除
List<String> 告诉编辑器:列表参数使用String类型。但对于JVM来说:这是不存在的
JVM中不存在泛型类和泛型方法,任何限定类型都被擦除,原则是:
(1)如果没有指定的限定类型 比如List<?> 那么使用Object替换;
(2)如果有限定类型,那么使用限定类型边界的第一个替换。比如 Demo<T extends A & B &C> 那么使用 A来替换
JVM 根据这一原则对泛型类、泛型方法进行翻译。这有可能导致出现一些情况,比如JVM中会存在两个相同的方法签名
针对这种情况,JVM会生成桥方法,保证类的多态性。总之有如下规律
- 任何泛型在编译过程中就被擦除类型,不管是泛型类、泛型方法、泛型表达式
- 所有类型参数都被 [限定类型]列表中第一个替换,如果没有指定限定类型,则用Object替换
- 桥方法被合成用于保持Java多态性
- 为保证类型安全性,必要时插入强制类型转换
4.泛型的约束
(1)泛型类:类型参数不能使用基本类型
(2)运行时类型:getClass 或者 instanceof 操作,都不能获取到限定类型,因为类型擦除的原因
(3)泛型类不能扩展异常类. 注意: 类型参数是可以扩展的,是泛型类不能扩展
(4)泛型数组:不能声明参数化的泛型类型数组.(注意:是不能声明参数化的)
(5)泛型类:类型参数不能实例化
(6)不能声明静态化的限定类型
(7)注意些特殊的写法
package simple;
import java.lang.reflect.Array;
import java.util.Date;
public class Pair2<T> {
private T max;
private T min;
public Pair2(T max, T min) {
this.max = max;
this.min = min;
}
public String toString() {
return String.format("{max:%s,min:%s}", this.max, this.min);
}
public static void main(String[] args) {
// 1.泛型类:类型参数不能使用基本类型
// 类型擦除之后,基本类型被Object替换,比如 Pair2<Object>
// 而Object只能存储Double这种包装类的引用,不能存储double基本类型的值
// 因此泛型类不能使用基本类型
// Pair2<int> pair = new Pair2(10, 2);
// 2.运行时类型
// getClass 或者 instanceof 操作,都不能获取到限定类型,因为类型擦除的原因
Pair2<Integer> pair1 = new Pair2<Integer>(10, 1);
Pair2<String> pair2 = new Pair2<String>("z", "a");
// 打印出来都是 class.simple.Pair2 也是因为类型擦除的原因
System.out.println(pair1.getClass());
System.out.println(pair2.getClass());
// 3.泛型类不能扩展异常类. 注意: 类型参数是可以扩展的,是泛型类不能扩展
//class Test<U> extends Throwable{
// }
// 在catch中捕获限定类型的对象也是不允许的
// class Test<U extends Throwable> {
// {
// try{
// }catch(U u) {
// }
// }
// }
// 在方法中可以使用限定类型变量,但一样的不允许在catch中捕获
class Test<U extends Throwable> {
public void test(U u) {
try{
// catch 捕获泛型对象仍然是错误的
// } catch(U e) {
} catch (Throwable e) {
try {
// 使用方法变量
throw u;
} catch (Throwable e1) {
e1.printStackTrace();
}
}
}
}
// 4.泛型数组:不能声明参数化的泛型类型数组.注意:是不能声明参数化的
// Pari2<String>[] pp = new Pair2<String>[10];
// 也是因为类型擦除,导致 Pair2<Stirng>被Pair2<Object>替换
// 那么实际上就是 Pair2<Object> pp = new Pair2<Object>[]
// 这样一来也可以转换成 Object[] objPair = pp;
// 而Object[]可以这样 objPair[0] = "haha"; 实际上你要求数组存储的是 Pair 类型,这样一来运行过程肯定报错
// 就类似于下面这一段
Integer[] ii = new Integer[10];
Object[] objII = ii;
// 编译虽然不会报错,但实际上是无法运行的:因为 ii 是Integer类型,所以objII也是Integer类型
// 这是因为:Java是强类型语言,要求数组记住元素类型,并且数组元素类型必须一致
//objII[0] = "hello";
// System.out.println(objII[0]);
// 除非Object类型数组指向的本身就是 Object[]类型的地址引用
Object[] obj2 = new Object[2];
obj2[0] = "str";
obj2[1] = new Date();
System.out.println(obj2[0].getClass() + "---" + obj2[1].getClass());
// 也可以利用反射创建泛型数组
Pair2<String>[] pairs = (Pair2<String>[]) Array.newInstance(Pair2.class, 10);
pairs[0] = new Pair2<String>("z", "a");
System.out.println(pairs);
// 或者非参数化的泛型数组也可以定义
Pair2[] pairs2 = new Pair2[2];
// 5.泛型类:类型参数不能实例化
class Test1<T>{
public void test(T t){
// 使用限定类型实例化不允许
// T tt = new T();
try {
// 但是利用反射可以这样实例化泛型对象
T tt = (T)t.getClass().newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
// 6. 也不能声明静态化的限定类型
// static T t;
// 7. 也不能这么写:因为限定类型被擦除后,本质上就变成了 public boolean equals(Object t)
// 因为所有类都是Object的子类,意味着 Test1 本身就有了一个 equals(Object t) 方法
// 因此这么些是不合法的
/*
public boolean equals(T t) {
return false;
}
*/
}
class Test2 {
// 针对上述第7点,这么写是合法的,这是覆盖父类的方法
// 而如果是用限定类型区作为equals参数,则导致 Test1 类中相当于有2个这样的方法
public boolean equals(Object t) {
return false;
}
}
}
}