8.1.1 类型参数的好处
ArrayList 类有一个类型参数用来指示元素的类型:ArrayList<String> files = new ArrayList<String>();
这使得代码具有更好的可读性。人们一看就知道这个数组列表中包含的是 String 对象。
在不用类型参数时,在调用get时不需要进行强制转换。在add时,编译器进行检查是否为String(原来Object可添加任意对象)。
8.2 定义简单泛型类
public class PairTest1 {
public static void main(String[] args) {
String[] words = {
"Mary", "had", "a", "little", "lamb"};
Pair<String> mn = ArrayAlg.minmax(words);
System.out.println("min = " + mn.getFirst());
System.out.println("max = " + mn.getSecond());
}
}
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);
}
}
class Pair<T> {
private String min;
private String max;
Pair(String a, String b) {
min = a;
max = b;
}
public String getFirst() {
return min;
}
public String getSecond() {
return max;
}
}
8.3 泛型方法
class ArrayAlg
{
public static <T> T getMiddle(T... a)
{
return a[a.length / 2];
}
}
可以定义在普通类中,也可以定义在泛型类中。
调用泛型方法时,
String middle = ArrayAlg.<String>getMiddle("John", "Q.", "Public");
也可以省略<>中的类型参数
String middle = ArrayAlg.getHiddle("John", "Q.", "Public");
8.4 类型变量的限定
可以对类型变量设置限定,使得实现某个接口。
public static <T extends Comparable> T min(T[] a) . . .
可以有多个限定,例如T extends Comparable & Serializable
限定类型用“ &” 分隔,而逗号用来分隔类型变量。
8.6 约束与局限性
8.6.1 不能用基本类型实例化类型参数
只有Pair<Double>
,没有Pair<double>
。因为类型擦除。Object和double不能互相转换。
8.6.2 运行时类型查询只适用于原始类型
由于编译的时候就把类型擦除了,所以在运行时并不存在泛型一说,只有相应的原始类型,如Pair在编译后就变成了Pair,所以在查询类型的时候只有原始类型。
如Pair<String>
和Pair<Employee>
getClass返回相同原始类型。
8.6.3 不能创建参数化类型的数组
不能实例化参数化类型的数组, 例如:
Pair<String>[] table = new Pair<String>[10]; // Error
这有什么问题呢? 擦除之后, table 的类型是 Pair[]。可以把它转换为 Object[]:
Object[] objarray = table;
数组在创建的时候就必须知道其内部元素的类型,并且会记住这个元素的类型,每次添加新的元素的时候都会检查一下,如果试图往里面添加其它类型的元素就会抛出一个ArrayStoreException异常,而类型擦除会使这种机制无效。
8.6.4 可变参数的警告
8.6.5 不能实例化类型变量
8.6.6 不能构造泛型数组
8.6.7 泛型类的静态上下文中类型变量无效
8.6.8 不能抛出或捕获泛型类的实例
8.6.9 可以消除对受查异常的检查
8.6.10 注意擦除后的冲突
8.8.1 通配符概念
通配符类型中, 允许类型参数变化。 例如, 通配符类型
Pair<? extends Employee>
表示任何泛型 Pair 类型, 它的类型参数是 Employee 的子类, 如 Pair< Manager>, 但不是 Pair< String>。
8.8.2 通配符的超类型限定
8.8.3 无限定通配符
Pair<?> 和 Pair 本质的不同在于: 可以用任意 Object 对象调用原始 Pair 类的 setObject
方法。
总结例子
import java.time.LocalDate;
public class PairTest
{
public static void main(String[] args)
{
Manager ceo = new Manager("Gus Greedy", 80000, 2003, 12, 15);
Manager cfo = new Manager("Sid Sneaky", 60000, 2003, 12, 11);
Pair<Manager> buddies = new Pair<>(ceo, cfo);
printBuddies(buddies);
ceo.setBonus(100000);
cfo.setBonus(500000);
Manager[] managers = {
ceo, cfo};
Pair<Employee> result = new Pair<>();
minmaxBonus(managers, result);
System.out.println("first: " + result.getFirst().getName() + ", second: " + result.getSecond().getName());
maxminBonus(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 minmaxBonus(Manager[] a, Pair<? super Manager> result)
{
if (a == null || a.length == 0)
return ;
Manager min = a[0];
Manager max = a[0];
for (int i = 1; i < a.length; i++)
{
if (min.getBonus() > a[i].getBonus())
min = a[i];
if (max.getBonus() < a[i].getBonus())
max = a[i];
}
result.setFirst(min);
result.setSecond(max);
}
public static void maxminBonus(Manager[] a, Pair<? super Manager> result)
{
minmaxBonus(a, result);
PairAlg.swapHelper(result);
}
}
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);
}
}
class Pair<T> //定义了一个泛型类,该类引入了一个类型变量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; }
}
class Employee
{
private String name;
private double salary;
private LocalDate hireDay;
public Employee(String name, double salary, int year, int month, int day)
{
this.name = name;
this.salary = salary;
hireDay = LocalDate.of(year, month, day);
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public LocalDate getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
}
class Manager extends Employee
{
private double bonus;
public Manager(String name, double salary, int year, int month, int day)
{
super(name, salary, year, month, day);
bonus = 0;
}
public double getSalary()
{
double baseSalary = super.getSalary();
return baseSalary + bonus;
}
public void setBonus(double b)
{
bonus = b;
}
public double getBonus()
{
return bonus;
}
}