一、泛型类
- 泛型类:
package pair1;
//一个泛型类就是具有一个或多个类型变量的类,Pair<T, U>{...}
//一般使用E表示集合的元素类型,K和V分别表示表的关键字与值的类型,T(或U、S等)表示“任意类型”
public class Pair<T> {
private T first;
private T second;
public Pair() {
}
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 first) {
this.first = first;
}
public void setSecond(T second) {
this.second = second;
}
}
- 使用方法类:
package pair1;
public class ArrayAlg {
public static Pair<String> minmax(String[] a) {
if (a == null || a.length == 0)
return null;
String min = a[0];
String max = a[0];
for (int i = 1; i < a.length; i++){
if (min.compareTo(a[i]) > 0)
min = a[i];
if (max.compareTo(a[i]) < 0)
max = a[i];
}
return new Pair<>(min, max);
}
}
- 测试:
package pair1;
public class PairTest {
public static void main(String[] args) {
String[] words = {"a", "Mary", "had", "little", "lamb"};
Pair<String> mm = ArrayAlg.minmax(words);
System.out.println("min = " + mm.getFirst());
System.out.println("max = " + mm.getSecond());
}
}
二、泛型方法
package pair1;
public class ArrayAlg {
//泛型方法可以定义在普通类中,也可以定义在泛型类中
//类型变量放在修饰符后面,返回类型的前面
public static <T> T getMiddle(T...a) {
return a[a.length/2];
}
}
三、类型变量的限定
泛型类:
package pair2;
public class Pair2<T> {
private T first;
private T second;
public Pair2() {
}
public Pair2(T first, T second) {
this.first = first;
this.second = second;
}
public T getFirst() {
return first;
}
public T getSecond() {
return second;
}
public void setFirst(T first) {
this.first = first;
}
public void setSecond(T second) {
this.second = second;
}
}
限定类:
package pair2;
class ArrayAlg2 {
//可以对类型变量T设置限定<T extends BoundingType>,T应该是绑定类型的子类型,T和绑定类型可以是类,也可以是接口
//一个类型变量或通配符可以有多个限定,T extends Comparable & Serializable,限定类型用&分隔,用逗号分隔类型变量
//Java继承中可以有多个接口超类型,只能有一个类,且类要在限定列表中的第一个
public static <T extends Comparable> Pair2<T> minmax(T[] a){
if (a == null || a.length == 0)
return null;
T min = a[0];
T max = a[0];
for (int i = 1; i < a.length; i++){
if (min.compareTo(a[i]) > 0)
min = a[i];
if (max.compareTo(a[i]) < 0)
max = a[i];
}
return new Pair2<>(min, max);
}
}
测试:
package pair2;
import java.time.LocalDate;
public class PairTest2 {
public static void main(String[] args) {
LocalDate[] birthday = {
LocalDate.of(1906, 12, 9),
LocalDate.of(1815, 12, 10),
LocalDate.of(1903, 12, 3),
LocalDate.of(1910, 6, 22),
};
Pair2<LocalDate> mm = ArrayAlg2.minmax(birthday);
System.out.println("min = " + mm.getFirst());
System.out.println("max = " + mm.getSecond());
}
}
四、泛型代码和虚拟机
虚拟机没有泛型类型变量,所有的对象都属于普通类。
1. 类型擦除
无论何时定义一个泛型类型,都自动提供了一个相应的原始类型。擦除类型变量,原始类型用第一个限定的类型变量来替换,如果没有给定限定就用Object替换。
Pair泛型类型:
public class Pair<T> {
private T first;
private T second;
public Pair() {
}
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 first) {
this.first = first;
}
public void setSecond(T second) {
this.second = second;
}
}
Pair原始类型:
public class Pair {
private Object first;
private Object second;
public Pair() {
}
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 first) {
this.first = first;
}
public void setSecond(Object second) {
this.second = second;
}
}
Interval泛型类型:
import java.io.Serializable;
public class Interval<T extends Comparable & Serializable> implements Serializable {
private T lower;
private T upper;
public Interval(T first, T second){
if (first.compareTo(second) <= 0){
lower = first;
upper = second;
}
else {
lower = second;
upper = first;
}
}
}
Interval原始类型:
import java.io.Serializable;
public class Interval implements Serializable {
private Comparable lower;
private Comparable upper;
public Interval(Comparable first, Comparable second){
if (first.compareTo(second) <= 0){
lower = first;
upper = second;
}
else {
lower = second;
upper = first;
}
}
}
2. 翻译泛型表达式
当程序调用泛型方法,如果擦除返回类型,编译器插入强制类型转换。
Pair<Employee> buddies = ...;
Employee buddy = buddies.getFirst();
擦除类型后将返回Object类型。编译器自动插入Employee的强制类型转换。
3. 翻译泛型方法
泛型方法:
public static <T extends Comparable> T min(T[] a) {}
擦除后:
public static Comparable min(Comparable[] a) {}
类型擦除与多态发生了冲突,需要在编译器在类中生成一个桥方法。
- 虚拟机中没有泛型,只有普通的类和方法;
- 所有的类型参数都用它们的限定类型替换;
- 桥方法被合成来保持多态;
- 为保持类型安全性,必要时插入强制类型转换。
4. 约束与局限性
- 不能用基本类型实例化类型参数,即没有Pair,只有Pair;
- 运行时类型查询只适用于原始类型,即不能a instanceof Pair;
- 不能创建参数化类型的数组,不能创建这样的数组Pair[] table = new pair[10],可以声明Pair[];
- Varargs(可变参数)警告;
- 不能实例化类型变量,不能使用new T(),new T[]或T.class这样的表达式中的类型变量。
- 不能构造泛型数组
- 泛型类的静态上下文中类型变量无效;
- 不能抛出或捕获泛型类的实例;
- 可以消除对受查异常的检查;
- 注意擦除后的冲突;
5. 泛型类型的继承规则
Pair不是Pair的子类,他们之间没有联系,他们都是Pair的子类;
五、通配符类型
1. 概念
通配符类型中,允许类型参数变化,如:Pair<? extends Employee>
表示任何泛型的Pair类型,它的类型参数时Employee子类。
2. 通配符的超类型限定
? super Manager
这个通配符限制为Manager的所有超类型。
带有超类型限定的通配符可以向泛型对象写入,带有子类型限定的通配符可以从泛型对象读取。
3. 无限定通配符
Pair<?>和Pair的本质不同在于:可以用任意的Object对象调用原始Pair类的setObject方法。
4. 通配符捕获
public static void swap(Pair<?> p) {
swapHelper(p);
}
//捕获,编译器必须能够确信通配符表达夫人是单个、确定的类型
public static <T> void swapHelper(Pair<T> p) {
T t = p.getFirst();
p.setFirst(p.getSecond());
p.setSecond(t);
}
5. 实例
Pair类:
package pair3;
public class Pair<T> {
private T first;
private T second;
public Pair() {
}
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 first) {
this.first = first;
}
public void setSecond(T second) {
this.second = second;
}
}
PairAlg类:
package pair3;
public class PairAlg {
public static boolean hasNulls(Pair<?> p) {
return p.getFirst() == null || p.getSecond() == null;
}
public static void swap(Pair<?> p) {
swapHelper(p);
}
public static <T> void swapHelper(Pair<T> p) {
T t = p.getFirst();
p.setFirst(p.getSecond());
p.setSecond(t);
}
}
测试:
package pair3;
import test.Employee;
import test.Manager;
public class PairTest {
public static void main(String[] args) {
Manager ceo = new Manager("Siri", 8000);
Manager cfo = new Manager("Xiao", 6000);
Pair<Manager> buddies = new Pair<>(ceo, cfo);
printBuddies(buddies);
Manager[] managers = {ceo, cfo};
Pair<Employee> result = new Pair<>();
minmaxSalary(managers, result);
System.out.println("first: " + result.getFirst().getName() + ", second: " + result.getSecond().getName());
maxminSalary(managers, result);
System.out.println("first: " + result.getFirst().getName() + ", second: " + result.getSecond().getName());
}
public static void printBuddies(Pair<? extends Employee> p) {
Employee first = p.getFirst();
Employee second = p.getSecond();
System.out.println(first.getName() + " and " + second.getName() + " are buddies.");
}
public static void minmaxSalary(Manager[] a, Pair<? super Manager> result) {
if (a.length == 0)
return;
Manager min = a[0];
Manager max = a[0];
for (int i = 1; i < a.length; i++){
if (min.getSalary() > a[i].getSalary())
min = a[i];
if (max.getSalary() < a[i].getSalary())
max = a[i];
}
result.setFirst(min);
result.setSecond(max);
}
public static void maxminSalary(Manager[] a, Pair<? super Manager> result) {
minmaxSalary(a, result);
PairAlg.swapHelper(result);
}
}
运行结果:
Siri and Xiao are buddies.
first: Xiao, second: Siri
first: Siri, second: Xiao