教材
《Java核心技术·卷I 开发基础(原书第12版)》
《Java语言程序设计(第3版)》
思维导图
一、泛型类(接口)
1.1 定义
泛型类(接口)是有一个或多个类型参数的类(接口)
public class ClassName<T> {
private T field1;
private T field2;
...
public T getField1() {
return field1;
}
public T getField2() {
return field2;
}
...
public void setField1(T newValue) {
field1 = newValue;
}
public void setField2(T newValue) {
field2 = newValue;
}
...
}
1.2 类型参数
- 传入该参数 用于指定类方法的返回类型以及字段和局部变量的类型
- 用尖括号 “ <> ” 括起来
- 用逗号分隔设置多个类型参数 “ <..., ..., ...> ”
public class ClassName<T, U, V, ...> {
... // 类体
}
常用的类型参数名
- E(集合的元素类型)
- K (表示键)
- V(表示值)
- N(表示数字)
- T(表示类型)
1.3 实例化
使用 new 操作符 用具体的类型替换类型变量
...... // 省略具体代码
ArrayList<String> nameList = new ArrayList<String>();
..... // 省略具体代码
二、泛型方法
2.1 定义
带有类型参数的方法
public class ClassName {
...
public [static] <T> T methodName([paramList]) {
... // 方法体
}
...
}
2.2 调用
把具体类型包围在尖括号中 放在方法名前面
可以省略具体类型和尖括号 编译器可以推断出所需要的类型
returnType variable = ClassName.<returnType>methodName([paramList]);
returnType variable = ClassName.methodName([paramList]); // 省略具体类型和尖括号
2.3 注意
- 可以在普通类中定义泛型方法 也可以在泛型类中定义
- 泛型方法可以定义为静态和非静态的
三、类型限定
3.1 定义
对泛型类(接口)和泛型方法的类型参数加以约束
public class ClassName<T extends BoundingType> {
... // 类体
}
public class ClassName<T super BoundingType> {
... // 类体
}
3.2 限定
- 关键字 extends:
- < T extends BoundingType>
- 类型变量 T 必须是限定类型 BoundingType 的子类型
- 关键字 super
- < T super BoundingType>
- 类型变量 T 必须是限定类型 BoundingType 的子类型
- 一个类型变量可以有多个限定:< T extends BoundingType1 & BoundingType2>
四、类型擦除
虚拟机没有泛型 只有普通的类和方法
在编译时 编译器将清除泛型类(接口)和泛型方法中所有与类型参数有关的信息
其中的类型参数会被替换为其限定类型(无限定时替换为 Object )
为保持类型安全性 必要时编译器会插入强制类型转换
会合成桥方法来保持多态
五、通配符类型
5.1 泛型类(接口)的继承规则
若两个泛型类(接口)中的类型参数存在继承层次结构 这两个泛型类之间没有任何(继承)关系
泛型类(接口)可以扩展(实现其他泛型接口)
5.2 通配符
使用通配符使类型参数可变(不固定)
5.3 通配符限定
涉及类的继承
5.3.1 子类型通配符限定
- < ? extends BoundingType>:泛型类型是限定类型 BoundingType 以及其子类
- 用来读取数据:读取数据的类型一定是限定类型 BoundingType 或其子类 (超类可以引用子类)
- 不能写入数据(可以写入 null):写入数据的类型具体是限定类型 BoundingType 还是其子类是不清楚的
5.3.2 超类型通配符限定
- < ? super BoundingType>:泛型类型是限定类型 BoundingType 以及其父类
- 用来写入数据:写入数据的类型一定是限定类型 BoundingType 或其子类(子类可以转换为超类)
- 不能读取数据(可以读取 Object):读取数据的类型具体是限定类型 BoundingType 或其父类或其子类是不清楚的
5.4 通配符无限定
5.5 通配符捕获
六、反射和泛型
七、限制与局限
- 不能用基本类型实例化类型参数
- 运行时类型查询只适用于原始类型:
- 使用 instanceof 判读一个实例对象是否是某一个泛型类型的实例时会产生警告或编译错误
- 对一个泛型类型使用 getClass 方法总是返回原始类型
- 不能创建参数化类型的数组
- 不能实例化类型变量:
- 不能在类似 new T(...) 的表达式中使用类型变量
- 在 Java 8 之后可以提供一个构造器表达式解决
- 不能构造泛型数组:T[] variable == new T[1]; // Error
- 不能在静态字段或静态方法中引用类型变量
- 不能抛出或捕获泛型类的实例
- 泛型类扩展 Throwable 是不合法的
- 可以利用泛型取消对检查型异常的检查
编程练习
public class Point<T> {
T x;
T y;
public Point(T x, T y) {
this.x = x;
this.y = y;
}
public void setX(T x) {
this.x = x;
}
public void setY(T y) {
this.y = y;
}
public T getX() {
return x;
}
public T getY() {
return y;
}
public void translate(T dx, T dy) {
if (x instanceof Number && y instanceof Number && dx instanceof Number && dy instanceof Number) {
if (x instanceof Integer) {
x = (T) Integer.valueOf(((Number) x).intValue() + ((Number) dx).intValue());
y = (T) Integer.valueOf(((Number) y).intValue() + ((Number) dy).intValue());
} else if (x instanceof Double) {
x = (T) Double.valueOf(((Number) x).doubleValue() + ((Number) dx).doubleValue());
y = (T) Double.valueOf(((Number) y).doubleValue() + ((Number) dy).doubleValue());
}
} else {
x = dx;
y = dy;
}
}
public String toString() {
return "Point{x=" + x + ",y=" + y + "}";
}
public static void main(String[] args) {
Point<Integer> intPoint = new Point<>(3, 4);
System.out.println("The original integer point:" + intPoint);
intPoint.translate(6, 8);
System.out.println("The translated integer point:" + intPoint);
Point<Double> doublePoint = new Point<>(2.82, 3.14);
System.out.println("The original double point:" + doublePoint);
doublePoint.translate(1.3, 5.2);
System.out.println("The translated double point:" + doublePoint);
}
}
import java.util.List;
import java.util.ArrayList;
interface Media {}
interface Book extends Media {}
interface Video extends Media {}
interface Newspaper extends Media {}
public class Library<T> {
private List<T> resources = new ArrayList<>();
public void addMedia(T x) {
resources.add(x);
}
public T retrieveLast() {
int size = resources.size();
if (size > 0) {
return resources.get(size - 1);
}
return null;
}
}