8.1 为什么要使用泛型程序设计
- 为了使程序的可读性更强
- 可以对多种不同类型的对象重用
- 编译器可以利用泛型的类型信息,进行检查
8.2 定义简单泛型类
泛型类就是有一个或多个类型变量的类:
例如: ```java package generic;
/**
Created by ql on 2022/5/30 */ public class Pair
{ 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 void setFirst(T first) {
this.first = first;
}
public T getSecond() {
return second;
}
public void setSecond(T second) {
this.second = second;
} }
泛型类可以有多个类型变量<br />Java库使用E表示集合元素,K和V分别表示表的键和值,T(或者U和S)表示“任意类型”```javapublic class Pair<T, U> {...}
package generic;/*** Created by ql on 2022/5/30*/public class PairTest1 {public static void main(String[] args) {String[] words = {"Mary", "had", "a", "little", "lamb"};Pair<String> mm = ArrayAlg.minmax(words);System.out.println("min=" + mm.getFirst());System.out.println("max=" + mm.getSecond());}}class ArrayAlg {/*** 得到String数组中的最小值和最大值* @param a* @return*/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 = 0; 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);}}
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.getMiddle("John", "Q.", "Public");
偶尔,编译器也会报错:
double middle = ArrayAlg.getMiddle(3.14, 1729, 0);
8.4 类型变量的限定
class ArrayAlg {public static <T extends Comparable> T min(T[] a) {if (a == null || a.length == 0) return null;T smallest = a[0];for (int i = 0; i < a.length; i++) {if (smallest.compareTo(a[i]) > 0) smallest = a[i];}return smallest;}}
只有T 继承了 Comparable接口,T类型才有compareTo方法
如果一个类型变量有多个限定,可以用“&”分隔:
T extends Comparable & Serializable
public static <T extends Comparable> Pair<T> minmax(T[] a) {if (a == null || a.length == 0) return null;T min = a[0];T max = a[0];for (int i = 0; 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);}
public class PairTest2 {public static void main(String[] args) {LocalDate[] birthdays = {LocalDate.of(1960, 12, 9),LocalDate.of(1815, 12, 10),LocalDate.of(1903, 12, 3),LocalDate.of(1910, 6, 22)};Pair<LocalDate> mm = ArrayAlg.minmax(birthdays);System.out.println("min=" + mm.getFirst());System.out.println("max=" + mm.getSecond());}}
8.5 泛型代码和虚拟机(了解)
定义一个泛型类型的时候会自动提供一个相应的原始类型(raw type),也就是去掉泛型类型名的类型
对于无限定类型的变量替换为Object
对于限定类型的变量用第一个限定来替换类型变量
…
8.6 限制与局限性
8.6.1 不能用基本类型实例化类型参数
没有Pair
原因:类型擦除
类型擦除后,Pair类含有Object类型的字段,而Object不能存储double
8.6.2 运行时类型查询只适用于原始类型
所有类型查询只产生原始类型:
if (a instanceof Pair<String>) // Errorif (a instanceof Pair<T>) //ErrorPair<String> p = (Pair<String>) a; //warning--can only test that a is a Pair
Pair<String> stringPair = ...;Pair<Employee> employeePair = ...;if(stringPair.getClass() == employeePair.getClass()) // equal
8.6.3 不能创建参数化类型的数组
var table = new Pair<String>[10]; //error
8.6.4 Varargs警告
考虑以下方法:
public static <T> void addAll(Collection<T> coll, T... ts) {for(T t : ts) coll.add(t);}
实际上ts是一个数组,包含提供的所有实参
考虑以下调用:
Collection<Pair<String>> table = ...;Pair<String> pair1 = ...;Pair<String> pair2 = ...;addAll(table, pair1, pair2);
为了调用addAll,虚拟机必须建立一个Pair
可以采用@SuppressWarnings(“unchecked”)来抑制警告
或者@SafeVarargs
8.6.5 不能实例化类型变量
// errorpublic Pair() {first = new T();second = new T();}
最好的解决:
Pair<String> p = Pair.makePair(String::new);// Supplier<T>是一个函数式接口,表示一个无参数而且返回类型为T的函数public static <T> Pair<T> makePair(Supplier<T> constr) {return new Pair<>(constr.get(), constr.get());}
传统的解决:
原理是通过反射调用Constructor.newInstance方法,但细节很复杂
first = T.class.getConstructor().newInstance(); //error
必须适当的设计API:
public static <T> Pair<T> makePair(Class<T> cl) {try {return new Pair<>(cl.getConstructor().newInstance(), cl.getConstructor().newInstance());}catch(Exception e) {return null;}}
该方法的调用:
Pair<Stirng> p = Pair.makePair(String.class);
8.6.6 不能构造泛型数组
…
8.7 泛型类型的继承规则
8.8 通配符类型
8.8.1 通配符概念
在通配符类型中,允许类型参数发生变化:
Pair<? extends Employee>
假设:
public static void printBuddies(Pair<Employee> p) {Employee first = p.getFirst();Employee second = p.getSecond();System.out.println(first.getName() + "and" + second.getName() + " are buddies.");}
由8.7可知,不能将Pair
解决方法:使用一个通配符类型:
public static void printBuddies(Pair<? extends Employee> p)

? extends Employee getFirst();void setFirst(? extends Employee);
编译器只知道需要Employee的某个子类型,但不知道具体什么类型,所以setFirst拒绝传递任何特定的类型
getFirst不存在该问题,因为把返回值赋值给一个Employee是完全合法的
8.8.2 通配符的超类型限定
? super Manager
这个通配符限制为Manager的所有超类型
void setFirst(? super Manager)? super Manager getFirst()
编译器无法知道setFirst方法的具体类型,所以不能接受Employee或Object,只能传递Manager或它的子类对象
调用getFirst,不能保证返回对象的类型,所以只能赋值给Object
public static void minmaxBouns(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.getBonus() > a[i].getBonus) min = a[i];if(max.getBonus() < a[i].getBonus) max = a[i];}result.setFirst(min);result.setSecond(max);}

直观的讲,带有超类型限定的通配符允许你写入一个泛型对象,而带有子类型限定的通配符允许你读取一个泛型对象
8.8.3 无限定通配符
例如:Pair<?>, 假设有以下方法
? getFirst();void setFirst(?);
getFirst只能赋值给Object。setFirst不能被调用
这样的脆弱的类型,看起来毫无用处,其实也有自己的应用场景
8.8.4 通配符捕获
public static void swap(Pair<?> p)
以下是非法的:
? t = p.getFirst();p.setFirst(p.getSecond());p.setSecond(t);
但是swap的时候必须临时保存第一个元素,解决方案:编写一个辅助方法:
public static <T> void swapHelper(Pair<T> p) {T t = p.getFirst();p.setFirst(p.getSecond);p.setSecond(t);}
现在可以由swap调用swapHelper:
public static swap(Pair<?> p) {swapHelper(p);}
swapHelper方法的参数T捕获通配符
综合
package pair3;/*** @version 1.01 2012-01-26* @author Cay Horstmann*/public class PairTest3{public static void main(String[] args){var ceo = new Manager("Gus Greedy", 800000, 2003, 12, 15);var cfo = new Manager("Sid Sneaky", 600000, 2003, 12, 15);var buddies = new Pair<Manager>(ceo, cfo);printBuddies(buddies);ceo.setBonus(1000000);cfo.setBonus(500000);Manager[] managers = { ceo, cfo };var result = new Pair<Employee>();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.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); // OK--swapHelper captures wildcard type}// can't write public static <T super manager> . . .}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);}}
8.9 反射和泛型
反射允许你在运行时分析任意对象
利用反射可以获得泛型类的哪些信息?
8.9.1 泛型class类
例如:String.class实际上是Class
8.9.2 使用Class参数进行类型匹配
8.9.3 虚拟机中的泛型类型信息

- Class类,描述具体类型
- TypeVariable接口,描述类型变量(如T extends … Comparable<? super T>)
- WildcardType接口,描述通配符(?super T)
- ParameterizedType接口,描述泛型类或接口类型(Comparable<? super T>)
- GenericArrayType接口,描述泛型数组 ```java package generic;
import java.lang.reflect.*; import java.util.Arrays; import java.util.Scanner;
/**
Created by ql on 2022/5/31 */ public class GenericRelfectionTest { public static void main(String[] args) {
String name;// 从命令行参数或用户输入中读取类名if (args.length > 0) name = args[0];else {try(var in = new Scanner(System.in)) {System.out.println("Enter class name: ");name = in.next();}}try {//打印class和public methods的泛型信息Class<?> cl = Class.forName(name);printClass(cl);for (Method m : cl.getDeclaredMethods()) {printMethod(m);}} catch (ClassNotFoundException e) {e.printStackTrace();}
}
private static void printMethod(Method m) {
String name = m.getName();System.out.print(Modifier.toString(m.getModifiers()));System.out.print(" ");printTypes(m.getTypeParameters(), "<", ", ", "> ", true);printType(m.getGenericReturnType(), false);System.out.print(" ");System.out.print(name);System.out.print("(");printTypes(m.getGenericParameterTypes(), "", ", ", "", false);System.out.println(")");
}
private static void printClass(Class<?> cl) {
System.out.print(cl);printTypes(cl.getTypeParameters(), "<", ",", ">", true);Type sc = cl.getGenericSuperclass();if (sc != null) {System.out.print(" extends ");printType(sc, false);}printTypes(cl.getGenericInterfaces(), " implements ", ", ", "", false);System.out.println();
}
public static void printType(Type type, boolean isDefinition) {
if (type instanceof Class) {var t = (Class<?>) type;System.out.print(t.getName());}else if (type instanceof TypeVariable) {var t = (TypeVariable<?>) type;System.out.print(t.getName());if (isDefinition)printTypes(t.getBounds(), " extends ", " & ", "", false);}else if (type instanceof WildcardType) {var t = (WildcardType) type;System.out.print("?");printTypes(t.getUpperBounds(), " extends ", " & ", "", false);printTypes(t.getLowerBounds(), " super ", " & ", "", false);}else if (type instanceof ParameterizedType) {var t = (ParameterizedType) type;Type owner = t.getOwnerType();if (owner != null) {printType(owner, false);System.out.print(".");}printType(t.getRawType(), false);printTypes(t.getActualTypeArguments(), "<", ", ", ">", false);}else if (type instanceof GenericArrayType) {var t = (GenericArrayType) type;System.out.print("");printType(t.getGenericComponentType(), isDefinition);System.out.print("[]");}
}
public static void printTypes(Type[] types, String pre, String sep, String suf, boolean isDefinition) {
if (pre.equals(" extends ") && Arrays.equals(types, new Type[] { Object.class }))return;if (types.length > 0) System.out.print(pre);for (int i = 0; i < types.length; i++){if (i > 0) System.out.print(sep);printType(types[i], isDefinition);}if (types.length > 0) System.out.print(suf);
} }
<a name="bcscE"></a>## 8.9.4 类型字面量```javapackage generic;import java.lang.reflect.Field;import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;import java.util.ArrayList;import java.util.HashMap;import java.util.Map;import java.util.function.Function;/*** Created by ql on 2022/5/31*/class TypeLiteral<T>{private Type type;/*** This constructor must be invoked from an anonymous subclass* as new TypeLiteral<. . .>(){}.*/public TypeLiteral(){Type parentType = getClass().getGenericSuperclass();if (parentType instanceof ParameterizedType){type = ((ParameterizedType) parentType).getActualTypeArguments()[0];}elsethrow new UnsupportedOperationException("Construct as new TypeLiteral<. . .>(){}");}private TypeLiteral(Type type){this.type = type;}/*** Yields a type literal that describes the given type.*/public static TypeLiteral<?> of(Type type){return new TypeLiteral<Object>(type);}public String toString(){if (type instanceof Class) return ((Class<?>) type).getName();else return type.toString();}public boolean equals(Object otherObject){return otherObject instanceof TypeLiteral&& type.equals(((TypeLiteral<?>) otherObject).type);}public int hashCode(){return type.hashCode();}}/*** Formats objects, using rules that associate types with formatting functions.*/class Formatter{private Map<TypeLiteral<?>, Function<?, String>> rules = new HashMap<>();/*** Add a formatting rule to this formatter.* @param type the type to which this rule applies* @param formatterForType the function that formats objects of this type*/public <T> void forType(TypeLiteral<T> type, Function<T, String> formatterForType){rules.put(type, formatterForType);}/*** Formats all fields of an object using the rules of this formatter.* @param obj an object* @return a string with all field names and formatted values*/public String formatFields(Object obj)throws IllegalArgumentException, IllegalAccessException{var result = new StringBuilder();for (Field f : obj.getClass().getDeclaredFields()){result.append(f.getName());result.append("=");f.setAccessible(true);Function<?, String> formatterForType = rules.get(TypeLiteral.of(f.getGenericType()));if (formatterForType != null){// formatterForType has parameter type ?. Nothing can be passed to its apply// method. Cast makes the parameter type to Object so we can invoke it.@SuppressWarnings("unchecked")Function<Object, String> objectFormatter= (Function<Object, String>) formatterForType;result.append(objectFormatter.apply(f.get(obj)));}elseresult.append(f.get(obj).toString());result.append("\n");}return result.toString();}}public class TypeLiterals{public static class Sample{ArrayList<Integer> nums;ArrayList<Character> chars;ArrayList<String> strings;public Sample(){nums = new ArrayList<>();nums.add(42); nums.add(1729);chars = new ArrayList<>();chars.add('H'); chars.add('i');strings = new ArrayList<>();strings.add("Hello"); strings.add("World");}}private static <T> String join(String separator, ArrayList<T> elements){var result = new StringBuilder();for (T e : elements){if (result.length() > 0) result.append(separator);result.append(e.toString());}return result.toString();}public static void main(String[] args) throws Exception{var formatter = new Formatter();formatter.forType(new TypeLiteral<ArrayList<Integer>>(){},lst -> join(" ", lst));formatter.forType(new TypeLiteral<ArrayList<Character>>(){},lst -> "\"" + join("", lst) + "\"");System.out.println(formatter.formatFields(new Sample()));}}


