Think in Java第四版 读书笔记9第15章 泛型

Think in Java第四版 读书笔记9第15章 泛型
泛型:适用于很多很多的类型
与其他语言相比 Java的泛型可能有许多局限 但是它还是有很多优点的。
本章介绍java泛型的局限和优势以及java泛型如何发展成现在这个样子的。


15.1 Java的泛型与C++比较

Java的语言设计灵感来自C++,虽然我们学习Java时不需要参考C++,但是有时与C++进行比较可以加深理解
泛型就是一个例子
之所以将Java的泛型与C++进行比较原因有二
1.比较后会理解泛型的基础,同时你会了解Java泛型的局限性以及为什么会有这些局限性。(理解某个技术不能做什么 才能更好地做到所能做到的,不必浪费时间在死胡同乱转–Think in JAVA作者讲的很精辟!)
2.人们对C++模板有一种误解,这种误解可能导致我们在理解泛型的意图产生偏差。


15.2 简单泛型-指定容器存储类型

泛型最常见的使用是在容器类上,让我们从最简单的开始吧
例子:存放单个指定类型对象的容器

class Automobile {}

public class Holder1 {
  private Automobile a;
  public Holder1(Automobile a) { this.a = a; }
  Automobile get() { return a; }
} ///:~

例子很简单,这个就是在类内部有个私有变量 存储指定类型的对象,这个也被称为组合关系,用的还是很广泛的,不过对于一个容器而言,他是不合格的,因为它的可重用性很低。每出现一个新的类型就要新增一个类

例子:存放单个任意类型对象的容器

public class Holder2 {
	private Object a;

	public Holder2(Object a) {
		this.a = a;
	}

	public void set(Object a) {
		this.a = a;
	}

	public Object get() {
		return a;
	}

	public static void main(String[] args) {
		Holder2 h2 = new Holder2(new Automobile());
		Automobile a = (Automobile) h2.get();
		h2.set("Not an Automobile");
		String s = (String) h2.get();
		h2.set(1); // 自动装箱是1.5之后才有的 使用低版本jdk会编译报错
		Integer x = (Integer) h2.get();
	}
} // /:~

可以看到 Holder2对象的实例先后存储了Automobile String Integer对象
通常 容器只会存储一种类型的对象,并且我们想做的是暂时不指定其存储对象的类型,等到使用时再指定。泛型能做到这一点,并且 泛型可以保证编译期对象类型的正确性。

例子 使用泛型,在使用时才指定容器存储类型
public class Holder3 {
private T a;

public Holder3(T a) {
	this.a = a;
}

public void set(T a) {
	this.a = a;
}

public T get() {
	return a;
}

public static void main(String[] args) {
	Holder3<Automobile> h3 = new Holder3<Automobile>(new Automobile());
	Automobile a = h3.get(); // No cast needed
	// h3.set("Not an Automobile"); // Error
	// h3.set(1); // Error
	Holder3<String> holder3 = new Holder3<String>("abc");
	String string = holder3.get();
}

} ///:~
像这样 在创建Holder3对象时必须指定存储的类型,跟存储Object相比,泛型在编译期就可以确定存放和取出的对象类型,
泛型的一个核心作用是告诉编译器使用的类型


15.2.1 一个一元组类库

由于return只能返回一个对象,那么要实现返回多个对象的需求 如何实现呢?这里就可以使用元组的概念了。
我们可以创建一个对象A 该对象A持有需要返回的多个其他对象,返回那一个对象A,就可以实现返回多个对象的效果了,这也是元组的概念。另外,如果元组只允许读取不允许重新赋值或新增对象(只读)就可以叫做数据传送对象/信使
元组可以是任意长度 任意类型的,我们举一个2维元组的例子

public class TwoTuple<A, B> {
	public final A first;
	public final B second;

	public TwoTuple(A a, B b) {
		first = a;
		second = b;
	}

	public String toString() {
		return "(" + first + ", " + second + ")";
	}
} // /:~

注意这里没有set get方法,原因是first second都是public的 可以直接访问,但是无法修改,因为他们都是final的,这就实现了只读的效果,并且比较简明
书中提到如果程序可以修改first second的内容,上面这种方式更安全,因为如果需要存储另外元素的元组 就需要创建另外的元组。 但是我看使用get set也能实现,不明白。。。 如果说按照后面讲述的内容便于扩展倒是可以理解。。。
例子:利用继承实现长度更长的元组

public class ThreeTuple<A,B,C> extends TwoTuple<A,B> {
  public final C third;
  public ThreeTuple(A a, B b, C c) {
    super(a, b);
    third = c;
  }
  public String toString() {
    return "(" + first + ", " + second + ", " + third +")";
  }
} ///:~

例子:使用元组(返回元组)

class Amphibian {
}

public class TupleTest {
	static TwoTuple<String, Integer> f() {
		// Autoboxing converts the int to Integer:
		return new TwoTuple<String, Integer>("hi", 47);
	}

	static ThreeTuple<Amphibian, String, Integer> g() {
		return new ThreeTuple<Amphibian, String, Integer>(new Amphibian(),
				"hi", 47);
	}

	public static void main(String[] args) {
		TwoTuple<String, Integer> ttsi = f();
		System.out.println(ttsi);
		// ttsi.first = "there"; // Compile error: final
		System.out.println(g());
	}
} /*
 * Output: (80% match) (hi, 47) (Amphibian@1f6a7b9, hi, 47)
 */// :~

在上述例子中 返回时的new语句似乎有点烦,后面会将他优化


15.2.2一个堆栈类(使用泛型实现自定义Stack)

//不使用LinkedList 而使用自定义的内部链式存储机制来实现stack

public class LinkedStack<T> {
	private static class Node<U> {// 模拟链表节点
		U item;// 当前节点内容
		Node<U> next;// 链表的下一个节点

		Node() {// 默认构造函数 当前内容与下一节点都为null 用于初始化末端哨兵
			item = null;
			next = null;
		}

		Node(U item, Node<U> next) {// 构造函数 参数*2
			this.item = item;
			this.next = next;
		}

		boolean end() {// 链表的当前和下一个节点均为空 则链表为空
			return item == null && next == null;
		}
	}

	private Node<T> top = new Node<T>(); // End sentinel末端哨兵 初始化为空节点

	// 该节点一直在栈顶

	public void push(T item) {// 入栈操作
		top = new Node<T>(item, top);// 将栈顶指针从上次的栈顶指向现在的item所在Node
	}

	public T pop() {// 弹栈操作
		T result = top.item;// 获取栈顶元素
		if (!top.end())// 链表不为空
			top = top.next;// 指针下移
		return result;
	}

	public static void main(String[] args) {
		LinkedStack<String> lss = new LinkedStack<String>();
		for (String s : "Phasers on stun!".split(" ")){
			lss.push(s);
		}
		lss.push(null);
		String s;
		while (!lss.top.end()){//个人觉得这样写更合适
			s = lss.pop();
			System.out.println(s);
		}
//		while ((s = lss.pop()) != null)//书中的写法
//			System.out.println(s);
	}
} /*
 * Output: 
null
stun!
on
Phasers

 */// :~

15.2.3 RandomList(使用泛型创建随机list)

public class RandomList<T> {
	// 存储特定类型对象的容器 内部包含一个ArrayList
	private ArrayList<T> storage = new ArrayList<T>();
	private Random rand = new Random(47);

	public void add(T item) {// 新增item
		storage.add(item);
	}

	public T select() {// 随机取出一个元素
		return storage.get(rand.nextInt(storage.size()));
	}

	public static void main(String[] args) {
		RandomList<String> rs = new RandomList<String>();
		for (String s : ("The quick brown fox jumped over the lazy brown dog")
				.split(" ")) {
			rs.add(s);
		}
		for (int i = 0; i < 11; i++) {
			System.out.print(rs.select() + " ");
		}
	}
} /*
 * Output: 
 * brown over fox quick quick dog brown The brown lazy brown
 */// :~

15.3 泛型接口

生成器(generator)负责创建对象 有点类似工程设计模式中的工厂方法。不过,一般工厂方法需要传递参数而生成器不需要。(生成器不需要额外信息就知道如何生成对象)
一般生成器只包含一个next方法 例如:

public class Coffee {
	private static long counter = 0;
	private final long id = counter++;

	public String toString() {
		return getClass().getSimpleName() + " " + id;
	}
} // /:~

Coffee及其子类:

public class Coffee {
	private static long counter = 0;
	private final long id = counter++;

	public String toString() {
		return getClass().getSimpleName() + " " + id;
	}
} // /:~

package generics.coffee;
public class Americano extends Coffee {} ///:~
package generics.coffee;
public class Breve extends Coffee {} ///:~
package generics.coffee;
public class Cappuccino extends Coffee {} ///:~
package generics.coffee;
public class Latte extends Coffee {} ///:~
package generics.coffee;
public class Mocha extends Coffee {} ///:~

实现泛型接口的类

public class CoffeeGenerator implements Generator<Coffee>, Iterable<Coffee> {
	private Class[] types = { Latte.class, Mocha.class, Cappuccino.class,
			Americano.class, Breve.class, };
	private static Random rand = new Random(47);

	public CoffeeGenerator() {//构造方法1
	}

	// For iteration: 
	private int size = 0;

	public CoffeeGenerator(int sz) {//构造方法2 
		size = sz;
	}

	public Coffee next() {
		try {
			//随机返回一种Coffee
			return (Coffee) types[rand.nextInt(types.length)].newInstance();
			// Report programmer errors at run time:
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	//自定义迭代器
	class CoffeeIterator implements Iterator<Coffee> {
		int count = size;

		public boolean hasNext() {
			return count > 0;
		}

		public Coffee next() {
			count--;
			return CoffeeGenerator.this.next();
		}

		public void remove() { // Not implemented
			throw new UnsupportedOperationException();
		}
	};

	//实现Iterable的方法
	public Iterator<Coffee> iterator() {
		return new CoffeeIterator();
	}

	public static void main(String[] args) {
		CoffeeGenerator gen = new CoffeeGenerator();
		for (int i = 0; i < 5; i++)
			System.out.println(gen.next());
		for (Coffee c : new CoffeeGenerator(5))//实现了Iterable所以可以使用for循环
			System.out.println(c);
	}
} /*
 * Output:
Americano 0
Latte 1
Americano 2
Mocha 3
Mocha 4
Breve 5
Americano 6
Latte 7
Cappuccino 8
Cappuccino 9
 */// :~

Generator接口的另一种实现的例子 该例子负责生成斐波那契数列

// Generate a Fibonacci sequence.
import net.mindview.util.*;

public class Fibonacci implements Generator<Integer> {
	private int count = 0;

	public Integer next() {
		return fib(count++);
	}

	private int fib(int n) {//当n比较大时 递归效率很低
		if (n < 2){//第0 和第1个数 返回1
			return 1;
		}
		return fib(n - 2) + fib(n - 1);//递归调用
	}

	public static void main(String[] args) {
		Fibonacci gen = new Fibonacci();
		for (int i = 0; i < 18; i++){//生成18个斐波那契数列
			System.out.println(gen.next() + " ");
		}
	}
} /*
 * Output: 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
 */// :~

上面这个例子我们看到我们实现的是Generator 但实际使用的却是int基本类型。也就是说Java泛型有一个局限性:基本类型无法使用泛型
但是Java SE 5 已经实现了自动装箱和自动拆箱的功能,所以基本类型会与对应的对象类型自动转换。

如果想要实现可以在for循环使用的Fibonacci 我们有两种做法 一个是用Fibonacci直接实现Iterable,或者继承Fibonacci并实现Iterable。
第一种实现是我们可以修改Fibonacci类的情况 第二章实现是我们不可以或者不想修改Fibonacci类的情况
第二种实现又叫适配器模式(实现某个接口以达到满足某些方法的类型要求,详见:https://blog.csdn.net/u011109881/article/details/82288922)
第二种实现的例子:

// Adapt the Fibonacci class to make it Iterable.
import java.util.*;

public class IterableFibonacci extends Fibonacci implements Iterable<Integer> {
	private int n;

	public IterableFibonacci(int count) {//参数用于判断是否遍历结束
		n = count;
	}

	public Iterator<Integer> iterator() {
		return new Iterator<Integer>() {
			public boolean hasNext() {
				return n > 0;
			}

			public Integer next() {
				n--;
				return IterableFibonacci.this.next();
			}

			public void remove() { // Not implemented
				throw new UnsupportedOperationException();
			}
		};
	}

	public static void main(String[] args) {
		for (int i : new IterableFibonacci(18))
			System.out.print(i + " ");
	}
} /*
 * Output: 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
 */// :~

15.4 泛型方法

到目前位置 我们使用的泛型都是作用在类上的 泛型同样可以作用于方法,即泛型方法。一个类是否有泛型方法与是否是泛型类没有关系
如果使用泛型方法就可以达到目的 那么使用泛型方法而不是使用泛型类(使整个类泛型化)。这样的结构更清晰。
另外需要注意静态方法需要使用泛型能力只能使其成为泛型方法(泛型类的泛型无法使用在静态方法上)
原因:

public class Atest<T> {
	//static void testA(T t){}//编译报错:Cannot make a static reference to the non-static type T
	static <K> void testA(K k){}//泛型方法
}

泛型方法:在返回值前加上泛型参数列表
例子:泛型方法的定义

public class GenericMethods {
  public <T> void f(T x) {
    System.out.println(x.getClass().getName());
  }
  public static void main(String[] args) {
    GenericMethods gm = new GenericMethods();
    gm.f("");
    gm.f(1);
    gm.f(1.0);
    gm.f(1.0F);
    gm.f('c');
    gm.f(gm);
  }
} /* Output:
java.lang.String
java.lang.Integer
java.lang.Double
java.lang.Float
java.lang.Character
GenericMethods
*///:~

GenericMethods类本身不是泛型类 但是其中的方法f()却是泛型方法
当使用泛型类时 我们必须指定其参数化类型 但是使用泛型方法时 通常不需要指定类型,编译器自动会找出指定的类型。这就是类型参数推断(Type argument inference)
这样在调用f方法时 我们可以传入不同的类型参数,看起来就像该方法被重载了无数次,可以接受任意类型参数
传入基本类型时 自动装箱机制会起到作用,将基本类型转换成指定的包装类类型


15.4.1 类型推断的限制

虽然编译器可以做一些类型推断 但是仅限于有限的情况,如果比较复杂,就不行了 比如
Map<Coffee, List<? extends Coffee>> coffeeMap = new HashMap<Coffee, List<? extends Coffee>>();
我们就需要重复写2次冗长的参数类型
有没有方法避免这个呢?
我们可以写一个工具类来生成一些容器

import java.util.*;

public class New {
	public static <K, V> Map<K, V> map() {
		return new HashMap<K, V>();
	}

	public static <T> List<T> list() {
		return new ArrayList<T>();
	}

	public static <T> LinkedList<T> lList() {
		return new LinkedList<T>();
	}

	public static <T> Set<T> set() {
		return new HashSet<T>();
	}

	public static <T> Queue<T> queue() {
		return new LinkedList<T>();
	}

	// Examples:
	public static void main(String[] args) {
		Map<String, List<String>> sls = New.map();
		List<String> ls = New.list();
		LinkedList<String> lls = New.lList();
		Set<String> ss = New.set();
		Queue<String> qs = New.queue();
	}
} // /:~

有了上述的工具类 我们的声明就简单了

Map<Coffee, List<? extends Coffee>> coffeeMap1 = New.map();

即可
看起来我们的工具类似乎起到一定的简化作用 但是真的这样吗,我们的初始目的是简化类型的声明,但是其他人在阅读代码时 还需要阅读New类的作用,这似乎与不使用new类时的效率不相上下。
从上面我们也可以看到 类型推断只在赋值时起作用,其他时候并不起作用。
如果你将泛型方法的返回值作为参数传递给另一个方法 类型推断将会失效。
比如下面的例子

public class LimitsOfInference {
	static void f(Map<Coffee, List<? extends Coffee>> coffees) {
	}

	public static void main(String[] args) {
		//f(New.map()); // Does not compile
	}
} // /:~

将New.map()的返回值传递给f方法 会编译报错 此时,编译器认为New.map()返回值被赋值给一个Object类型的变量
所以将f方法修改如下 会编译通过

static void f(Object coffees) {
}

显示的类型说明(很少使用)
即显示地指明类型
具体做法:在点操作符后面插入类型声明 比如
new1.<Coffee, List> map() (new1是New的实例 此时map方法不是静态方法)
特别的
1)使用在定义该方法的类时要使用this关键字 即类似

this.<Coffee, List<Coffee>> map()

2)使用static的方法 必须在点操作符前加上类名即类似

New.<Coffee, List<Coffee>> map() 

(此时map方法是静态方法)

map方法是静态方法的显示的类型说明 案例

public class ExplicitTypeSpecification {
	static void f(Map<Coffee, List<Coffee>> coffee) {
	}

	public static void main(String[] args) {
		f(New.<Coffee, List<Coffee>> map());
	}
} // /:~

要明确 显示的类型说明仅使用在非赋值语句


15.4.2 可变参数与泛型方法

泛型方法与可变参数列表可以很好的共存

public class GenericVarargs {
  public static <T> List<T> makeList(T... args) {//可变参数结合泛型的方法
    List<T> result = new ArrayList<T>();
    for(T item : args)
      result.add(item);
    return result;
  }
  public static void main(String[] args) {
    List<String> ls = makeList("A");
    System.out.println(ls);
    ls = makeList("A", "B", "C");
    System.out.println(ls);
    ls = makeList("ABCDEFFHIJKLMNOPQRSTUVWXYZ".split(""));
    System.out.println(ls);
  }
} /* Output:
[A]
[A, B, C]
[, A, B, C, D, E, F, F, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z]
*///:~

makeList方法的作用与java.util.Arrays.asList()方法一致 (可以把makeList替换成Arrays.asList)


15.4.3 用于Generator的泛型方法

泛型结合Collection的案例

import generics.coffee.*;
import java.util.*;
import net.mindview.util.*;

public class Generators {
    //注意这里使用了接口Generator 它的实现有CoffeeGenerator Fibonacci
	public static <T> Collection<T> fill(Collection<T> coll, Generator<T> gen,
			int n) {
		for (int i = 0; i < n; i++)
			coll.add(gen.next());
		return coll;
	}

	public static void main(String[] args) {
		Collection<Coffee> coffee = fill(new ArrayList<Coffee>(),
				new CoffeeGenerator(), 4);
		for (Coffee c : coffee)
			System.out.println(c);
		Collection<Integer> fnumbers = fill(new ArrayList<Integer>(),
				new Fibonacci(), 12);
		for (int i : fnumbers)
			System.out.print(i + ", ");
	}
} /*
 * Output: 
Americano 0
Latte 1
Americano 2
Mocha 3
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 
 */// :~

15.4.4 一个通用的Genertor

public interface Generator<T> {//这就是泛型接口
	T next();
} // /:~
//使用该生成器需要2个条件 
//1.使用者是public类
//2.使用者拥有无参构造函数(默认构造方法)
public class BasicGenerator<T> implements Generator<T> {
  private Class<T> type;
  public BasicGenerator(Class<T> type){ this.type = type; }
  public T next() {
    try {
      // 假设 type 是一个public类(否则报错):
      return type.newInstance();
    } catch(Exception e) {
      throw new RuntimeException(e);
    }
  }
  // Produce a Default generator given a type token:
  public static <T> Generator<T> create(Class<T> type) {
    return new BasicGenerator<T>(type);
  }
} ///:~

使用这个通用的Genertor的案例

public class CountedObject {
	private static long counter = 0;
	private final long id = counter++;

	public long id() {
		return id;
	}

	public String toString() {
		return "CountedObject " + id;
	}
} ///:~
public class BasicGeneratorDemo {
	public static void main(String[] args) {
		Generator<CountedObject> gen = BasicGenerator
				.create(CountedObject.class);
		for (int i = 0; i < 5; i++)
			System.out.println(gen.next());
	}
} /*
 * Output: CountedObject 0 CountedObject 1 CountedObject 2 CountedObject 3
 * CountedObject 4
 */// :~

15.4.5 简化元组的使用(元组优化)

使用类型推断+static方法 优化元组工具,使其更通用的工具类库
结合15.2.1的各种元组类 用Tuple统一结合起来专门生成对象

public class Tuple {
	public static <A, B> TwoTuple<A, B> tuple(A a, B b) {
		return new TwoTuple<A, B>(a, b);
	}

	public static <A, B, C> ThreeTuple<A, B, C> tuple(A a, B b, C c) {
		return new ThreeTuple<A, B, C>(a, b, c);
	}
} // /:~

使用:

  static TwoTuple f2() { return tuple("hi", 47); }
  static ThreeTuple<Amphibian,String,Integer> g() {
    return tuple(new Amphibian(), "hi", 47);
  }

15.4.6 一个Set实用工具
//表示数学里面的关系

public class Sets {
	//a并b
	public static <T> Set<T> union(Set<T> a, Set<T> b) {
		Set<T> result = new HashSet<T>(a);
		result.addAll(b);
		return result;
	}

	//a交b
	public static <T> Set<T> intersection(Set<T> a, Set<T> b) {
		Set<T> result = new HashSet<T>(a);
		result.retainAll(b);
		return result;
	}
	
	//去掉superset中 superset与subset的交集
	public static <T> Set<T> difference(Set<T> superset, Set<T> subset) {
		Set<T> result = new HashSet<T>(superset);
		result.removeAll(subset);
		return result;
	}

	// A并B 去掉 A交B
	public static <T> Set<T> complement(Set<T> a, Set<T> b) {
		return difference(union(a, b), intersection(a, b));
	}
} // /:~

public enum Watercolors {
	A, B, C, D, E, F, G, 
	H, I, J, K, L, M, N, 
	O, P, Q, R, S, T, 
	U, V, W, X, Y, Z
} // /:~
public class WatercolorSets {
	public static void main(String[] args) {
		Set<Watercolors> set1 = EnumSet.range(A, N);
		Set<Watercolors> set2 = EnumSet.range(H, T);
		print("set1: " + set1);
		print("set2: " + set2);
		print("union(set1, set2): " + union(set1, set2));
		Set<Watercolors> subset = intersection(set1, set2);
		print("intersection(set1, set2): " + subset);
		print("difference(set1, subset): " + difference(set1, subset));
		print("difference(set2, subset): " + difference(set2, subset));
		print("complement(set1, set2): " + complement(set1, set2));
	}
} /*
 * Output: 
set1: [A, B, C, D, E, F, G, H, I, J, K, L, M, N]
set2: [H, I, J, K, L, M, N, O, P, Q, R, S, T]
union(set1, set2): [D, E, C, K, Q, M, S, G, P, N, B, I, O, T, A, J, L, H, F, R]
intersection(set1, set2): [K, M, N, I, J, L, H]
difference(set1, subset): [D, E, C, G, B, A, F]
difference(set2, subset): [Q, S, P, O, T, R]
complement(set1, set2): [D, E, C, Q, S, G, P, B, O, T, A, F, R]
 */// :~

例子:对比各种集合类的异同

public class ContainerMethodDifferences {
	static Set<String> methodSet(Class<?> type) {//将集合类的方法存储到TreeSet 存储在set为了去重
		Set<String> result = new TreeSet<String>();
		for (Method m : type.getMethods())
			result.add(m.getName());
		return result;
	}

	static void interfaces(Class<?> type) {//将接口方法存储在ArrayList
		System.out.print("Interfaces in " + type.getSimpleName() + ": ");
		List<String> result = new ArrayList<String>();
		for (Class<?> c : type.getInterfaces())
			result.add(c.getSimpleName());
		System.out.println(result);
	}

	static Set<String> object = methodSet(Object.class);//存储Object所有方法
	static {
		object.add("clone");
	}

	static void difference(Class<?> superset, Class<?> subset) {
		System.out.print(superset.getSimpleName() + " extends "
				+ subset.getSimpleName() + ", adds: ");
		//调用之前定义的difference方法
		Set<String> comp = Sets.difference(methodSet(superset),
				methodSet(subset));
		comp.removeAll(object); //去掉Object的所有方法
		System.out.println(comp);
		interfaces(superset);//打印接口方法
	}

	public static void main(String[] args) {
		System.out.println("Collection: " + methodSet(Collection.class));
		interfaces(Collection.class);
		difference(Set.class, Collection.class);
		difference(HashSet.class, Set.class);
		difference(LinkedHashSet.class, HashSet.class);
		difference(TreeSet.class, Set.class);
		difference(List.class, Collection.class);
		difference(ArrayList.class, List.class);
		difference(LinkedList.class, List.class);
		difference(Queue.class, Collection.class);
		difference(PriorityQueue.class, Queue.class);
		System.out.println("Map: " + methodSet(Map.class));
		difference(HashMap.class, Map.class);
		difference(LinkedHashMap.class, HashMap.class);
		difference(SortedMap.class, Map.class);
		difference(TreeMap.class, Map.class);
	}
} // /:~
/**
Collection: [add, addAll, clear, contains, containsAll, equals, forEach, hashCode, isEmpty, iterator, parallelStream, remove, removeAll, removeIf, retainAll, size, spliterator, stream, toArray]
Interfaces in Collection: [Iterable]
Set extends Collection, adds: []
Interfaces in Set: [Collection]
HashSet extends Set, adds: []
Interfaces in HashSet: [Set, Cloneable, Serializable]
LinkedHashSet extends HashSet, adds: []
Interfaces in LinkedHashSet: [Set, Cloneable, Serializable]
TreeSet extends Set, adds: [headSet, descendingIterator, descendingSet, pollLast, subSet, floor, tailSet, ceiling, last, lower, comparator, pollFirst, first, higher]
Interfaces in TreeSet: [NavigableSet, Cloneable, Serializable]
List extends Collection, adds: [replaceAll, get, indexOf, subList, set, sort, lastIndexOf, listIterator]
Interfaces in List: [Collection]
ArrayList extends List, adds: [trimToSize, ensureCapacity]
Interfaces in ArrayList: [List, RandomAccess, Cloneable, Serializable]
LinkedList extends List, adds: [offerFirst, poll, getLast, offer, getFirst, removeFirst, element, removeLastOccurrence, peekFirst, peekLast, push, pollFirst, removeFirstOccurrence, descendingIterator, pollLast, removeLast, pop, addLast, peek, offerLast, addFirst]
Interfaces in LinkedList: [List, Deque, Cloneable, Serializable]
Queue extends Collection, adds: [poll, peek, offer, element]
Interfaces in Queue: [Collection]
PriorityQueue extends Queue, adds: [comparator]
Interfaces in PriorityQueue: [Serializable]
Map: [clear, compute, computeIfAbsent, computeIfPresent, containsKey, containsValue, entrySet, equals, forEach, get, getOrDefault, hashCode, isEmpty, keySet, merge, put, putAll, putIfAbsent, remove, replace, replaceAll, size, values]
HashMap extends Map, adds: []
Interfaces in HashMap: [Map, Cloneable, Serializable]
LinkedHashMap extends HashMap, adds: []
Interfaces in LinkedHashMap: [Map]
SortedMap extends Map, adds: [lastKey, subMap, comparator, firstKey, headMap, tailMap]
Interfaces in SortedMap: [Map]
TreeMap extends Map, adds: [descendingKeySet, navigableKeySet, higherEntry, higherKey, floorKey, subMap, ceilingKey, pollLastEntry, firstKey, lowerKey, headMap, tailMap, lowerEntry, ceilingEntry, descendingMap, pollFirstEntry, lastKey, firstEntry, floorEntry, comparator, lastEntry]
Interfaces in TreeMap: [NavigableMap, Cloneable, Serializable]
**/

15.5 泛型运用在匿名内部类

public interface Generator<T> { T next(); } ///:~


public class Generators {
	public static <T> Collection<T> fill(Collection<T> coll, Generator<T> gen,
			int n) {
		for (int i = 0; i < n; i++)
			coll.add(gen.next());
		return coll;
	}
} 


class Customer {
	private static long counter = 1;
	private final long id = counter++;

	//私有化构造方法 只能通过Generator获取实例
	private Customer() {
	}

	public String toString() {
		return "Customer " + id;
	}

	//匿名内部类1
	//Customer对象生成器
	//generator方法每次调用会创建一个Generator对象 但这是不必要的
	public static Generator<Customer> generator() {
		return new Generator<Customer>() {
			public Customer next() {
				return new Customer();
			}
		};
	}
}

class Teller {
	private static long counter = 1;
	private final long id = counter++;

	//私有化构造方法 只能通过Generator获取实例
	private Teller() {
	}

	public String toString() {
		return "Teller " + id;
	}

	//匿名内部类2
	// 单例Generator对象:
	// 可以对比Customer的generator方法 这里只会创建一个generator实例
	public static Generator<Teller> generator = new Generator<Teller>() {
		public Teller next() {
			return new Teller();
		}
	};
}

public class BankTeller {
	public static void serve(Teller t, Customer c) {
		System.out.println(t + " serves " + c);
	}

	public static void main(String[] args) {
		Random rand = new Random(47);
		Queue<Customer> line = new LinkedList<Customer>();
		//生成15个Customer对象 放入line中
		Generators.fill(line, Customer.generator(), 15);
		List<Teller> tellers = new ArrayList<Teller>();
		//生成4个Teller对象 放入tellers中
		Generators.fill(tellers, Teller.generator, 4);
		for (Customer c : line){
			//遍历line中的Customer15个对象 从tellers取出随机的Teller与Customer进行匹配输出
			serve(tellers.get(rand.nextInt(tellers.size())), c);
		}
			
	}
} /*
 * Output:
Teller 3 serves Customer 1
Teller 2 serves Customer 2
Teller 3 serves Customer 3
Teller 1 serves Customer 4
Teller 1 serves Customer 5
Teller 3 serves Customer 6
Teller 1 serves Customer 7
Teller 2 serves Customer 8
Teller 3 serves Customer 9
Teller 3 serves Customer 10
Teller 2 serves Customer 11
Teller 4 serves Customer 12
Teller 2 serves Customer 13
Teller 1 serves Customer 14
Teller 1 serves Customer 15
 */// :~

15.6 构建复杂模型(组合与数组)

利用泛型可以轻松地将A B C D等不同数据结构组合起来构成一个新的数据结构,比如

public class TwoTuple<A,B> {
  public final A first;
  public final B second;
  public TwoTuple(A a, B b) { first = a; second = b; }
  public String toString() {
    return "(" + first + ", " + second + ")";
  }
} ///:~

另外一个例子是将泛型和数组结合

public class Generators {
	public static <T> Collection<T> fill(Collection<T> coll, Generator<T> gen,
			int n) {
		for (int i = 0; i < n; i++)
			coll.add(gen.next());
		return coll;
	}
}
class Product {
	private final int id;
	private String description;
	private double price;

	public Product(int IDnumber, String descr, double price) {
		id = IDnumber;
		description = descr;
		this.price = price;
		System.out.println(toString());
	}

	public String toString() {
		return id + ": " + description + ", price: $" + price;
	}

	public void priceChange(double change) {
		price += change;
	}

	public static Generator<Product> generator = new Generator<Product>() {
		private Random rand = new Random(47);

		//随机产生一个id<1000 描述为Test 价格为0-1000之间的 Product
		public Product next() {
			return new Product(rand.nextInt(1000), "Test", Math.round(rand
					.nextDouble() * 1000.0) + 0.99);
		}
	};
}

class Shelf extends ArrayList<Product> {//Shelf是一个Product数组
	public Shelf(int nProducts) {//产生nProducts个Product的ArrayList
		Generators.fill(this, Product.generator, nProducts);
	}
}

class Aisle extends ArrayList<Shelf> {//Aisle是一个Shelf数组
	public Aisle(int nShelves, int nProducts) {//创建长度为nShelves的ArrayList<Shelf> 每一个Shelf元素中填充了nProducts个Product
		for (int i = 0; i < nShelves; i++)
			add(new Shelf(nProducts));
	}
}

//class CheckoutStand {
//}
//
//class Office {
//}

public class Store extends ArrayList<Aisle> {//Store是一个Aisle数组
//	private ArrayList<CheckoutStand> checkouts = new ArrayList<CheckoutStand>();
//	private Office office = new Office();

	public Store(int nAisles, int nShelves, int nProducts) {
		for (int i = 0; i < nAisles; i++)
			add(new Aisle(nShelves, nProducts));
	}

	public String toString() {
		StringBuilder result = new StringBuilder();
		for (Aisle a : this)
			for (Shelf s : a)
				for (Product p : s) {
					result.append(p);
					result.append("\n");
				}
		return result.toString();
	}

	public static void main(String[] args) {
		System.out.println(new Store(14, 5, 10));
	}
} /*
258: Test, price: $400.99
861: Test, price: $160.99
868: Test, price: $417.99
207: Test, price: $268.99
551: Test, price: $114.99
278: Test, price: $804.99
520: Test, price: $554.99
...
 */// :~

这个例子像是List的嵌套
Store本身是一个list 包含A个Aisle
Aisle本身是一个list 包含B个Shelf
Shelf本身是一个list 包含C个Product
因此一个Store可以包含ABC个Product


15.7 擦除的神秘之处

//ArrayList<String>与ArrayList<Integer>是否是不同的类型呢?
//我们可以将String放入ArrayList<String> 却不能放入ArrayList<Integer>
//所以他们是不同的类型? 但是输出结果似乎出乎意料
public class ErasedTypeEquivalence {
	public static void main(String[] args) {
		Class c1 = new ArrayList<String>().getClass();
		Class c2 = new ArrayList<Integer>().getClass();
		System.out.println(c1 == c2);
		System.out.println(Arrays.toString(c1.getTypeParameters()));
		System.out.println(Arrays.toString(c2.getTypeParameters()));
	}
} /*
 * Output: 
true
[E]
[E]
 */// :~

另一个补充案例

class Frob {}
class Fnorkle {}
class Quark<Q> {}
class Particle<POSITION,MOMENTUM> {}

public class LostInformation {
  public static void main(String[] args) {
    List<Frob> list = new ArrayList<Frob>();
    Map<Frob,Fnorkle> map = new HashMap<Frob,Fnorkle>();
    Quark<Fnorkle> quark = new Quark<Fnorkle>();
    Particle<Long,Double> p = new Particle<Long,Double>();
    System.out.println(Arrays.toString(
      list.getClass().getTypeParameters()));
    System.out.println(Arrays.toString(
      map.getClass().getTypeParameters()));
    System.out.println(Arrays.toString(
      quark.getClass().getTypeParameters()));
    System.out.println(Arrays.toString(
      p.getClass().getTypeParameters()));
  }
} /* Output:
[E]
[K, V]
[Q]
[POSITION, MOMENTUM]
*///:~

/**
 * 思考:
 * 根据JDK文档描述 我们可以通过调用Class.getTypeParameters方法来获得一个类型变量数据,该数组表示有泛型声明所生命的类型参数。
 * 但是假如如文档所说 我们看到的结果应该是
 * [Frob]
 * [Frob, Fnorkle]
 * [Fnorkle]
 * [Long, Double]
 * 而事实却非如此
 * 因此结论是:在泛型代码内部 无法获取任何有关泛型参数类型的信息
 * 
 */

从上述例子,我们看出
我们无法知道创建某个实例的实际类型参数
Java的泛型使用擦除来实现,这意味着你在使用泛型时。任何类型信息都被擦除了,你只知道在使用一个对象。所以List List在运行时实际是相同的类型。
这两种形式都被擦除成原生类型 即List。
本节将讨论java的泛型的擦除 这也是Java泛型学习的一个最大障碍


15.7.1 C++的方式

C++泛型例子

#include <iostream>
using namespace std;

template<class T> class Manipulator {
	T obj;//存储了类型T
public:
	Manipulator(T x) {
		obj = x;
	}
	void manipulate() {
		obj.f();//此处调用了f方法
	}
};

class HasF {
public:
	void f() {
		cout << "HasF::f()" << endl;
	}
};

int main() {
	HasF hf;
	Manipulator<HasF> manipulator(hf);//此处实例化Manipulator C++内部会查询HasF是否有方法f 如果没有则编译报错
	//C++的泛型模板代码知道模板参数的类型
	manipulator.manipulate();
} /* Output:
 HasF::f()
 ///:~

以上例子使用Java来写:

public class HasF {
	public void f() {
		System.out.println("HasF.f()");
	}
} // /:~



// 编译报错
class Manipulator<T> {
	private T obj;

	public Manipulator(T x) {
		obj = x;
	}

	// Error: 编译报错 The method f() is undefined for the type T
	public void manipulate() {
		obj.f();//由于类型擦除 Java无法将obj能调用f方法的需求映射到实际类型HasF上
		//为了能调用f方法 我们需要协助泛型类 给定泛型边界,告诉编译器遵循边界类型。
	}
}

public class Manipulation {
	public static void main(String[] args) {
		HasF hf = new HasF();
		Manipulator<HasF> manipulator = new Manipulator<HasF>(hf);
		manipulator.manipulate();
	}
} // /:~

我们对上述例子稍加修改 就可以编译成功了

//这里有了边界
class Manipulator2<T extends HasF> {//通过T extends HasF让Java编译器知道T也有HasF的方法f
	private T obj;

	public Manipulator2(T x) {
		obj = x;
	}

	public void manipulate() {
		obj.f();
	}
	
	public static void main(String[] args) {
		HasF hf = new HasF();
		Manipulator2<HasF> manipulator = new Manipulator2<HasF>(hf);
		System.out.println(Arrays.toString(manipulator.getClass().getTypeParameters()));
		manipulator.manipulate();
	}
} 
/**
 *输出:
[T]
HasF.f()
**/

但是 上述例子中 泛型没有多大作用,我们即使不使用泛型 仍可以写出代码

class Manipulator3 {
	private HasF obj;

	public Manipulator3(HasF x) {
		obj = x;
	}

	public void manipulate() {
		obj.f();
	}
	
	public static void main(String[] args) {
		HasF hf = new HasF();
		Manipulator3 manipulator = new Manipulator3(hf);
		manipulator.manipulate();
	}
} // /:~

因此 泛型需要判断是不是真的需要,泛型只有当你希望使用的类型参数比某个具体类型更加“泛化” 才需要使用。
比如下面这个例子泛型确实起到作用:

class ReturnGenericType<T extends HasF> {
	private T obj;

	public ReturnGenericType(T x) {
		obj = x;
	}

	public T get() {//将返回确切的类型 如果不使用泛型 只能返回HasF类型
		return obj;
	}
} // /:~

15.7.2 迁移兼容性(Java泛型使用擦除实现的由来)

Java的泛型不是一开始就有的产物,而是在SE 5.0引入的。之所以使用擦除,是为了兼容性。即使用了泛型的客户端仍然可以使用非泛型的类库,
并且使用了泛型的类库也可以使用在非泛型的客户端上。为了实现这一需求,Java采用擦除这一特性来实现泛型,即泛型只在特殊时期起作用,
过了这一时期,泛型就好像不存在一样,这样 不管程序是泛型的还是非泛型的,通通都可以看成没有使用泛型,也就不存在兼容性问题了。
为什么要兼容性:假设一个类库开发了很长时间,但是该类库不支持泛型,那么对于需要使用泛型的客户端,如果没有兼容性,该类库就废了。
泛型类型只在静态检查期间才出现,静态检查之后 所有泛型类型会被擦除,例如List会被擦除为List,普通的泛型类型会被擦除为Object。


15.7.3 擦除的问题(相比于其他语言 Java的泛型没有那么灵活)

Java实现泛型 需要从非泛化的代码向泛化代码转变 同时不能破坏现有类库。
擦除的代价是显著的,泛型只存在于静态检查,,而不能使用在运行时,比如强制类型转换 instanceof和new等操作符。在编写代码时,应该提醒自己
泛型只是看起来好像拥有参数类型信息,这只是暂时性的。

class GenericBase<T> {
	private T element;

	public void set(T arg) {
		arg = element;
	}

	public T get() {
		return element;
	}
}

class Derived1<T> extends GenericBase<T> {//可以使用泛型
}

class Derived2 extends GenericBase {//也可以不使用泛型
} // 没有报错

//class Derived3 extends GenericBase<?> {}
// Strange error:
// unexpected type found : ?
// required: class or interface without bounds
// 没明白。。。

public class ErasureAndInheritance {
	@SuppressWarnings("unchecked")//SE 5之后出现的注解 压制警告,不进行类型检查
	public static void main(String[] args) {
		Derived2 d2 = new Derived2();
		Object obj = d2.get();
		d2.set(obj); // 在这里出现警告,没有使用泛型来规定参数类型!
	}
} // /:~

15.7.4 边界处的动作

由于擦除 泛型有一个令人困惑的地方:可以表示没有任何意义的事物
例如:

public class ArrayMaker<T> {//泛型类
	private Class<T> kind;

	public ArrayMaker(Class<T> kind) {
		this.kind = kind;
	}

	@SuppressWarnings("unchecked")
	T[] create(int size) {
		return  (T[]) Array.newInstance(kind, size);
		//由于擦除 Array.newInstance实际返回的是Object 所以必须强制转换
	}

	public static void main(String[] args) {
		ArrayMaker<String> stringMaker = new ArrayMaker<String>(String.class);
		String[] stringArray = stringMaker.create(9);
		System.out.println(Arrays.toString(stringArray));
	}
} /*
 * Output: [null, null, null, null, null, null, null, null, null]
 */// :~

泛型使用在单个类型上

public class ListMaker<T> {
	List<T> create() {
		//虽然在调用new的时候 在运行时擦除了String的类型信息  
		//new ArrayList<T>()看起来写成new ArrayList()也无所谓 但这样编译器会警告,没有进行类型检查
		return new ArrayList<T>();
	}

	public static void main(String[] args) {
		//没有警告
		ListMaker<String> stringMaker = new ListMaker<String>();
		List<String> stringList = stringMaker.create();
	}
} ///:~

泛型使用在List

public class FilledListMaker<T> {
	List<T> create(T t, int n) {
		List<T> result = new ArrayList<T>();//擦除了类型
		for (int i = 0; i < n; i++){
			result.add(t);//但是还可以确保对象是T类型 这一点由编译器保证
		}
		return result;
	}

	public static void main(String[] args) {
		FilledListMaker<String> stringMaker = new FilledListMaker<String>();
		List<String> list = stringMaker.create("Hello", 4);
		System.out.println(list);
	}
} /*
 * Output: [Hello, Hello, Hello, Hello]
 */// :~

我们再对比一下使用泛型和没有泛型的编译结果:

public class SimpleHolder {
	private Object obj;

	public void set(Object obj) {
		this.obj = obj;
	}

	public Object get() {
		return obj;
	}

	public static void main(String[] args) {
		SimpleHolder holder = new SimpleHolder();
		holder.set("Item");
		String s = (String) holder.get();
	}
} 
/**

使用javac SimpleHolder.java编译出class文件之后
再使用javap -c SimpleHolder反编译
Compiled from "SimpleHolder.java"
public class SimpleHolder {
  public SimpleHolder();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void set(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #2                  // Field obj:Ljava/lang/Object;
       5: return

  public java.lang.Object get();
    Code:
       0: aload_0
       1: getfield      #2                  // Field obj:Ljava/lang/Object;
       4: areturn

  public static void main(java.lang.String[]);
    Code:
       0: new           #3                  // class SimpleHolder
       3: dup
       4: invokespecial #4                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: ldc           #5                  // String Item
      11: invokevirtual #6                  // Method set:(Ljava/lang/Object;)V
      14: aload_1
      15: invokevirtual #7                  // Method get:()Ljava/lang/Object;
      18: checkcast     #8                  // class java/lang/String
      21: astore_2
      22: return
}
 */
public class GenericHolder<T> {
	private T obj;

	public void set(T obj) {
		this.obj = obj;
	}

	public T get() {
		return obj;
	}

	public static void main(String[] args) {
		GenericHolder<String> holder = new GenericHolder<String>();
		holder.set("Item");
		String s = holder.get();
	}
} 
/**
C:\Users\hjcai\Desktop>javac GenericHolder.java

C:\Users\hjcai\Desktop>javap -c GenericHolder
Warning: Binary file GenericHolder contains generics.GenericHolder
Compiled from "GenericHolder.java"
public class generics.GenericHolder<T> {
  public generics.GenericHolder();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void set(T);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #2                  // Field obj:Ljava/lang/Object;
       5: return

  public T get();
    Code:
       0: aload_0
       1: getfield      #2                  // Field obj:Ljava/lang/Object;
       4: areturn

  public static void main(java.lang.String[]);
    Code:
       0: new           #3                  // class generics/GenericHolder
       3: dup
       4: invokespecial #4                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: ldc           #5                  // String Item
      11: invokevirtual #6                  // Method set:(Ljava/lang/Object;)V
      14: aload_1
      15: invokevirtual #7                  // Method get:()Ljava/lang/Object;
      18: checkcast     #8                  // class java/lang/String
      21: astore_2
      22: return
}
 */

可以看到他们的反编译结果是一样的,这也一定程度解释了java的泛型是如何做到兼容性的。Java的泛型更多的由编译器确保 而编译结果看起来就像是没有泛型一样。(个人观点,还是没理解书里说的边界是什么)


15.8 擦除的补偿

由于泛型的擦除 所以在泛型代码中某些操作能力会被丢失。

public class Erased<T> {
  private final int SIZE = 100;
  public static void f(Object arg) {
    if(arg instanceof T) {}          // Error
    T var = new T();                 // Error
    T[] array = new T[SIZE];         // Error
    T[] array = (T)new Object[SIZE]; // Unchecked warning
  }
} ///:~

可以看到 instanceof 以及 new操作符 都不能直接作用在泛型上,那么如何解决这一问题呢
既然运行时类型信息被擦除了,那么我们可以在擦除前保存类型信息

class Building {
}

class House extends Building {
}

public class ClassTypeCapture<T> {
	Class<T> kind;//用于保存类型信息

	public ClassTypeCapture(Class<T> kind) {//创建对象时保存类型信息
		this.kind = kind;
	}

	public boolean f(Object arg) {//判断类型的方法
		return kind.isInstance(arg);
	}

	public static void main(String[] args) {
		ClassTypeCapture<Building> ctt1 = new ClassTypeCapture<Building>(
				Building.class);
		System.out.println(ctt1.f(new Building()));//动态判断类型
		System.out.println(ctt1.f(new House()));
		ClassTypeCapture<House> ctt2 = new ClassTypeCapture<House>(House.class);
		System.out.println(ctt2.f(new Building()));
		System.out.println(ctt2.f(new House()));
	}
} /*
 * Output: true true false true
 */// :~

15.8.1 创建泛型类型的实例

Java不能创建泛型对象 new T()的原因有二
1.由于泛型擦除了类型信息
2.不知道具体的T是否具有默认无参构造函数
在C++中是如何创建泛型类型的对象的呢?

// C++, not Java!
// C++可以直接创建泛型类型对象因为它在编译期就会被检查
template<class T> class Foo {
  T x; // 创建一个类型为T的filed
  T* y; // 指向T的指针
public:
  // 初始化指针:
  Foo() { y = new T(); }//创建了一个泛型
};

class Bar {};

int main() {
  Foo<Bar> fb;
  Foo<int> fi; //对基本类型同样适用
} ///:~

如果我们想要像C++一样创建泛型类型 需要做额外工作:可以使用工厂对象

import static net.mindview.util.Print.*;
//不完善的工厂
class ClassAsFactory<T> {
	T x;//工厂保存了类型信息

	public ClassAsFactory(Class<T> kind) {
		try {
			x = kind.newInstance();//使用newInstance 创建实例 不过使用该方法前提是存在默认构造函数
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
}

class Employee {
}

public class InstantiateGenericType {
	public static void main(String[] args) {
		ClassAsFactory<Employee> fe = new ClassAsFactory<Employee>(
				Employee.class);
		print("ClassAsFactory<Employee> succeeded");
		try {
			ClassAsFactory<Integer> fi = new ClassAsFactory<Integer>(
					Integer.class);
		} catch (Exception e) {
			//创建Integer对象时失败 因为Integer没有默认无参构造函数,在调用newInstance时失败
			print("ClassAsFactory<Integer> failed");
		}
	}
} /*
 * Output: ClassAsFactory<Employee> succeeded ClassAsFactory<Integer> failed
 */// :~

对上述代码进行优化 使用显示的工厂

interface FactoryI<T> {
	T create();
}

class Foo2<T> {
	private T x;//保存类型信息

	public <F extends FactoryI<T>> Foo2(F factory) {
		x = factory.create();
	}
	// ...
}

class IntegerFactory implements FactoryI<Integer> {//专门创建Integer的工厂
	public Integer create() {
		return new Integer(0);//不使用newInstance创建对象 而使用new创建对象 以免异常
	}
}

class Widget {
	public static class Factory implements FactoryI<Widget> {//专门创建Widget的工厂
		public Widget create() {
			return new Widget();//不使用newInstance创建对象 而使用new创建对象 以免异常
		}
	}
}

public class FactoryConstraint {
	public static void main(String[] args) {
		new Foo2<Integer>(new IntegerFactory());
		new Foo2<Widget>(new Widget.Factory());
	}
} // /:~

使用模板方法可以达到同样的目的

abstract class GenericWithCreate<T> {
	final T element;// 用于保存类型信息

	GenericWithCreate() {
		System.out.println("1");
		element = create();//保存类型信息的实际地点
	}

	abstract T create();
}

class X {
}

class Creator extends GenericWithCreate<X> {
	X create() {//创建X对象的方法
		System.out.println("2");
		return new X();
	}
	
	Creator(){
		System.out.println("3");
	}

	void f() {
		System.out.println(element.getClass().getSimpleName());
	}
}

public class CreatorGeneric {
	public static void main(String[] args) {
		//尝试调用Creator默认函数,存在基类 先调用父类构造函数(point 1)
		//父类构造函数调用了create方法(point 2)
		//子类覆盖了create方法 因此实际调用子类create方法
		//调用子类构造函数(point 3)
		Creator c = new Creator();
		c.f();
	}
} /*
 * Output: 
1
2
3
X
 */// :~

但是不管是哪一种方式,他们都会通过保存类型信息来创建泛型对象


15.8.2 泛型数组

本节的例子有点多 讨论的内容有以下几点
1.想要创建泛型数组可以使用ArrayList代替
2.非要使用数组的情况 在内部使用Object 在返回时转型为泛型类型
3.可以在创建泛型数组时传递一个类型标记 用于恢复被擦除的类型
4.Java的源码中也有大量Object数组转型为泛型数组的代码 这会产生大量警告。因此即使代码是写在源码中的 也不代表这就是正确的写法

如前Erased.java所述 不能创建泛型数组,可以使用ArrayList代替
这里你可以获得数组的行为以及编译期的类型安全

public class ListOfGenerics<T> {
	private List<T> array = new ArrayList<T>();

	public void add(T item) {
		array.add(item);
	}

	public T get(int index) {
		return array.get(index);
	}
} // /:~

class Generic<T> {
}

public class ArrayOfGeneric {
	static final int SIZE = 100;
	// 编译器接受这样的声明 但却无法创建一个确切类型的数组
	static Generic<Integer>[] gia;

	@SuppressWarnings("unchecked")
	public static void main(String[] args) {
		// 可以编译 但是运行错误 [Ljava.lang.Object; cannot be cast to [Lgenerics.Generic;
		// gia = (Generic<Integer>[])new Object[SIZE];
		// 运行时类型是原始(擦除了)类型 即Object类型 
		gia = (Generic<Integer>[]) new Generic[SIZE];
		System.out.println(gia.getClass().getSimpleName());
		gia[0] = new Generic<Integer>();
		// gia[1] = new Object(); // 编译错误
		// 编译时发现类型不匹配
		// gia[2] = new Generic<Double>();
	}
} /*

public class GenericArray<T> {
	private T[] array;// 存储泛型类型

	@SuppressWarnings("unchecked")//如果警告是符合预期的 可以通过该注解忽略警告
	public GenericArray(int sz) {
		//无法直接 创建 T[] array = new T[size]
		//所以创建Object数组然后强转
		array = (T[]) new Object[sz];// 同样出现类型擦除 需要强转
	}

	public void put(int index, T item) {
		array[index] = item;
	}

	public T get(int index) {
		return array[index];
	}

	// 暴露底层表示的方法 返回类型T的数组 但是调用它时
	// Method that exposes the underlying representation:
	public T[] rep() {
		return array;
	}

	public static void main(String[] args) {
		GenericArray<Integer> gai = new GenericArray<Integer>(10);
		// 运行时错误
		// java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
		// Integer[] ia = gai.rep();
		// This is OK:
		Object[] oa = gai.rep();//gai.rep按理来讲是Integer数组 但是这只在编译时,运行时类型被擦除 只能看成Object数组
	}
} // /:~

//内部使用时用Object类型的优势在于 我们不太可能忘记运行时的类型 从而引入缺陷
public class GenericArray2<T> {
	private Object[] array;//内部使用时用Object类型

	public GenericArray2(int sz) {
		array = new Object[sz];//内部使用时用Object类型
	}

	public void put(int index, T item) {
		array[index] = item;
	}

	@SuppressWarnings("unchecked")
	public T get(int index) {//返回时才转型
		return (T) array[index];
	}

	@SuppressWarnings("unchecked")
	public T[] rep() {//返回时才转型
		return (T[]) array; // Warning: unchecked cast
	}

	public static void main(String[] args) {
		GenericArray2<Integer> gai = new GenericArray2<Integer>(10);
		for (int i = 0; i < 10; i++)
			gai.put(i, i);
		for (int i = 0; i < 10; i++)
			System.out.print(gai.get(i) + " ");
		System.out.println();
		try {
			Integer[] ia = gai.rep();
		} catch (Exception e) {
			System.out.println(e);
		}
	}
} /*
 * Output: (Sample) 0 1 2 3 4 5 6 7 8 9 java.lang.ClassCastException:
 * [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
 */// :~

import java.lang.reflect.*;
//使用一个类型标记
public class GenericArrayWithTypeToken<T> {
	private T[] array;

	@SuppressWarnings("unchecked")
	public GenericArrayWithTypeToken(Class<T> type, int sz) {//传递了一个类型标记Class<T> type,以便从类型擦除中恢复
		array = (T[]) Array.newInstance(type, sz);
	}

	public void put(int index, T item) {
		array[index] = item;
	}

	public T get(int index) {
		return array[index];
	}

	// Expose the underlying representation:
	public T[] rep() {
		return array;
	}

	public static void main(String[] args) {
		GenericArrayWithTypeToken<Integer> gai = new GenericArrayWithTypeToken<Integer>(
				Integer.class, 10);
		// This now works:
		Integer[] ia = gai.rep();
	}
} // /:~

15.9 边界

extends使用在泛型边界上和普通情况的例子

interface HasColor {
	java.awt.Color getColor();
}

class Colored<T extends HasColor> {
	T item;

	Colored(T item) {
		this.item = item;
	}

	T getItem() {
		return item;
	}

	// 边界允许你调用一个方法
	java.awt.Color color() {
		return item.getColor();
	}
}

class Dimension {
	public int x, y, z;
}

// 这不会起作用 -- 类必须在第一个 , 然后是接口:
// This won't work -- class must be first, then interfaces:
// class ColoredDimension<T extends HasColor & Dimension> {

// 多边界:
// 可以看到这里的extends和普通继承关系的 extends 不同
// 这里的extends被Java重写了
class ColoredDimension<T extends Dimension & HasColor> {//ColoredDimension持有一个T 该类型继承Dimension 实现HasColor
	T item;

	ColoredDimension(T item) {
		this.item = item;
	}

	T getItem() {
		return item;
	}

	java.awt.Color color() {// item实现了HasColor接口 因此可以调用getColor方法
		return item.getColor();
	}

	int getX() {
		return item.x;// item继承了Dimension
	}

	int getY() {
		return item.y;// item继承了Dimension
	}

	int getZ() {
		return item.z;// item继承了Dimension
	}
}

interface Weight {
	int weight();
}

// As with inheritance, you can have only one
// concrete class but multiple interfaces:
// 因为有继承 你可以extends一个类以及多个接口
// 注意,类只可以放在第一个位置 否则报错
// The type XXX is not an interface; it cannot be specified as a bounded
// parameter
class Solid<T extends Dimension & HasColor & Weight> {//Solid持有一个T 该类型继承Dimension 实现HasColor和Weight
	T item;

	Solid(T item) {
		this.item = item;
	}

	T getItem() {
		return item;
	}

	java.awt.Color color() {
		return item.getColor();
	}

	int getX() {
		return item.x;
	}

	int getY() {
		return item.y;
	}

	int getZ() {
		return item.z;
	}

	int weight() {
		return item.weight();
	}
}

//这里是通常使用的extends
class Bounded extends Dimension implements HasColor, Weight {
	public java.awt.Color getColor() {
		return null;
	}

	public int weight() {
		return 0;
	}
}

public class BasicBounds {
	public static void main(String[] args) {
		//Bounded extends Dimension implements HasColor, Weight
		//因此Bounded可以存储在Solid
		Solid<Bounded> solid = new Solid<Bounded>(new Bounded());
		solid.color();
		solid.getY();
		solid.weight();
	}
} // /:~

//例子 如何添加泛型边界限制//每个层次都加入边界限制

class HoldItem<T> {//HoldItem持有一个对象 item item类型没有限制
	T item;

	HoldItem(T item) {
		this.item = item;
	}

	T getItem() {
		return item;
	}
}

//前面的<T extends HasColor>是泛型边界限制 后面的HoldItem<T>是继承的意思
//Colored2继承HoldItem<T> 它也持有一个对象item  item限制为<T extends HasColor>
class Colored2<T extends HasColor> extends HoldItem<T> {
	Colored2(T item) {
		super(item);
	}

	java.awt.Color color() {
		return item.getColor();
	}
}

//ColoredDimension2继承Colored2<T> 它也持有一个对象item  item限制为<T extends Dimension & HasColor>
//当前类的限制<T extends Dimension & HasColor>其覆盖范围必须小于等于继承类的限制<T extends HasColor>
class ColoredDimension2<T extends Dimension & HasColor> extends Colored2<T> {
	ColoredDimension2(T item) {
		super(item);
	}

	int getX() {
		return item.x;
	}

	int getY() {
		return item.y;
	}

	int getZ() {
		return item.z;
	}
}

//进一步限制泛型类型
class Solid2<T extends Dimension & HasColor & Weight> extends
		ColoredDimension2<T> {
	Solid2(T item) {
		super(item);
	}

	int weight() {
		return item.weight();
	}
}

public class InheritBounds {
	public static void main(String[] args) {
		Solid2<Bounded> solid2 = new Solid2<Bounded>(new Bounded());
		solid2.color();
		solid2.getY();
		solid2.weight();
	}
} // /:~

//更多层次添加泛型边界限制示例
//Demonstrating bounds in Java generics.
import java.util.*;

interface SuperPower {// 超能力
}

interface XRayVision extends SuperPower {// 千里眼 透视
	void seeThroughWalls();
}

interface SuperHearing extends SuperPower {// 顺风耳
	void hearSubtleNoises();
}

interface SuperSmell extends SuperPower {// 嗅觉灵敏
	void trackBySmell();
}

class SuperHero<POWER extends SuperPower> {
	POWER power;// 超级英雄具有能力

	SuperHero(POWER power) {
		this.power = power;
	}

	POWER getPower() {
		return power;
	}
}

class SuperSleuth<POWER extends XRayVision> extends SuperHero<POWER> {// 能力限制为XRayVision
	SuperSleuth(POWER power) {
		super(power);
	}

	void see() {
		power.seeThroughWalls();
	}
}

class CanineHero<POWER extends SuperHearing & SuperSmell> extends
		SuperHero<POWER> {// 能力限制为SuperHearing和SuperSmell
	CanineHero(POWER power) {
		super(power);
	}

	void hear() {
		power.hearSubtleNoises();
	}

	void smell() {
		power.trackBySmell();
	}
}

class SuperHearSmell implements SuperHearing, SuperSmell {// 普通类
	public void hearSubtleNoises() {
	}

	public void trackBySmell() {
	}
}

class DogBoy extends CanineHero<SuperHearSmell> {// SuperHearSmell满足CanineHero泛型的限制
	DogBoy() {
		super(new SuperHearSmell());
	}
}

public class EpicBattle {
	// 边界在泛型方法的使用
	// Bounds in generic methods:
	static <POWER extends SuperHearing> void useSuperHearing(
			SuperHero<POWER> hero) {//返回类型限制为SuperHearing
		hero.getPower().hearSubtleNoises();
	}

	static <POWER extends SuperHearing & SuperSmell> void superFind(
			SuperHero<POWER> hero) {//返回类型限制为SuperHearing & SuperSmell
		hero.getPower().hearSubtleNoises();
		hero.getPower().trackBySmell();
	}

	public static void main(String[] args) {
		DogBoy dogBoy = new DogBoy();
		useSuperHearing(dogBoy);
		superFind(dogBoy);
		// You can do this:
		List<? extends SuperHearing> audioBoys;
		// But you can't do this: (因为通配符"?"是被限制为单一边界)
		// List<? extends SuperHearing & SuperSmell> dogBoys;
	}
} // /:~

15.10 通配符

我们在前面的章节和本章前部分使用过通配符?
在本节 我们会更深入地讨论该问题。
入手点是:可以向子类类型的数组赋予父类的数组引用。
例子:可以向子类类型的数组赋予父类的数组引用的例子(数组的协变)
//从本例子可以发现 数组的类型检查 在编译和运行时的类型检查可能不同

class Fruit {
}

class Apple extends Fruit {
}

class Jonathan extends Apple {
}

class Orange extends Fruit {
}

public class CovariantArrays {
	public static void main(String[] args) {
		// 此处使用向上转型 但是使用的时机不恰当
		Fruit[] fruit = new Apple[10];// 创建父类类型Fruit数组的引用,指向子类类型Apple数组
		fruit[0] = new Apple(); // OK
		fruit[1] = new Jonathan(); // OK
		// 运行时类型是 Apple[], not Fruit[] or Orange[]:
		try {
			// 编译时允许添加Fruit:
			// 编译时 由于本身是一个Fruit数组,所以允许添加任意fruit及其子类
			// 但是运行时发现是Apple数组,只能添加Apple及其子类
			fruit[0] = new Fruit(); // ArrayStoreException
		} catch (Exception e) {
			System.out.println(e);
		}
		try {
			// 编译时允许添加Oranges:
			fruit[0] = new Orange(); // ArrayStoreException
		} catch (Exception e) {
			System.out.println(e);
		}
	}
} /*
 * Output: java.lang.ArrayStoreException: Fruit java.lang.ArrayStoreException:
 * Orange
 */// :~

例子:泛型不支持协变

// {CompileTimeError} (Won't compile)
import java.util.*;

//将上一个例子中的类型错误检查移到编译时
public class NonCovariantGenerics {
	// Compile Error: 类型不匹配:
	// 不能将一个涉及Apple的容器 赋值给一个涉及Fruit的容器
	//因为像上一个例子那样
	//Apple的容器存放Apple及其子类 
	//Fruit容器存放Fruit及其子类 所以Fruit的List既可以放Apple 也可以放不是Apple的Fruit
	//因此Fruit的List 和 Apple的List不等价
	//这里讨论的是容器本身的类型 而不是容器持有的内容类型之间的关系
	List<Fruit> flist = new ArrayList<Apple>();
} // /:~

//使用通配符可以在两个类型建立向上转型的关系(通配符支持协变)
public class GenericsAndCovariance {
	public static void main(String[] args) {
		// 通配符允许协变:
		List<? extends Fruit> flist = new ArrayList<Apple>();
		// List<? extends Fruit> 可以理解为flist是一个List,该list的所有持有对象都是Fruit或者其子类
		// List<? extends Fruit> flist期望的引用是任意fruit或者其子类 但是它不关心具体是什么类型
		// 只要是fruit的子类即可
		// 由于通配符的向上转型功能 new ArrayList<Apple>();实际转型为new ArrayList<Fruit>()
		// ?又代表了不确定的类型 那么编译器就不知道实际存储的类型了 因此添加任何类型的对象都会报错

		// Compile Error: can't add any type of object:
		// flist.add(new Apple());
		// flist.add(new Fruit());
		// flist.add(new Object());
		flist.add(null); // Legal but uninteresting
		// We know that it returns at least Fruit:
		Fruit f = flist.get(0);
	}
} // /:~

解释可能不是很清楚 但是只要记住 使用了通配符声明的引用,无法调用任何参数类型为泛型的方法,因为它不知道当前的类型


15.10.1 编译器有多聪明

注意使用通配符之后 不是所有的方法都无法调用 而是方法参数为泛型类型的方法,无法调用 比如下面的例子

public class CompilerIntelligence {
  public static void main(String[] args) {
	  Apple apple = new Apple();
    List<? extends Fruit> flist =
      Arrays.asList(apple);
    //flist.add(new Apple());//compile error
    
    //E get(int index);
    Apple a = (Apple)flist.get(0); // No warning
    //boolean contains(Object o);
    System.out.println(flist.contains(apple));// Argument is 'Object'
    //int indexOf(Object o);
    System.out.println(flist.indexOf(apple));// Argument is 'Object'
  }
} ///:~

可以看到 如果参数是Object类型或者返回值是泛型类型,仍然可以调用

另一个例子

public class Holder<T> {
	private T value;

	public Holder() {
	}

	public Holder(T val) {
		value = val;
	}

	public void set(T val) {
		value = val;
	}

	public T get() {
		return value;
	}

	public boolean equals(Object obj) {
		return value.equals(obj);
	}

	public static void main(String[] args) {
		Holder<Apple> apple = new Holder<Apple>();
		Apple d = apple.get();
		apple.set(d);
		// Holder<Fruit> Fruit = apple; // 泛型不支持协变 通配符才支持
		Holder<? extends Fruit> fruit = apple; // OK
		Fruit p = fruit.get();//fruit继承自Fruit 可以向上转型
		d = (Apple) fruit.get(); // Returns 'Object' //开发者确保安全性
		try {
			Orange c = (Orange) fruit.get(); // No warning//开发者确保安全性
		} catch (Exception e) {
			System.out.println(e);
		}
		// fruit.set(new Apple()); // 使用了通配符,无法再使用泛型类型 Cannot call set()
		// fruit.set(new Fruit()); // 使用了通配符,无法再使用泛型类型 Cannot call set()
		System.out.println(fruit.equals(d)); // equals方法没有使用泛型 所以没有问题
	}
} /*
 * Output: (Sample) java.lang.ClassCastException: Apple cannot be cast to Orange
 * true
 */// :~

15.10.2 逆变(超类型通配符) (下界通配符)

之前我们使用的是<? extends MyClass> (extends后面的为上界)
现在我们可以使用<? super MyClass>甚至是<? super T>
可以读作任意是类型T的父类类型(super后面跟着的为下界)
(extends可以理解为“继承自” super可以理解为“的子类之一是”)
注意不可以声明为(T super MyClass)
超类型通配符使用案例

//class Fruit {
//}
//
//class Apple extends Fruit {
//}
//
//class Jonathan extends Apple {
//}
//
//class Orange extends Fruit {
//}

public class SuperTypeWildcards {
	static void writeTo(List<? super Apple> apples) {
		apples.add(new Apple());
		apples.add(new Jonathan());
		// apples.add(new Fruit()); // Error
	}
} // /:~

解释:writeTo方法的参数apples的类型不确定 但是知道是Apple的直接或间接父类,即Apple是类型下界(画个继承关系图更好理解)
既然它是apple的父类型,那么我们可以向该列表添加Apple或者其子类

超类型通配符(super)就是下界通配符
子类型通配符(extends)就是上界通配符

//使用下界通配符写入对象

public class GenericWriting {


	static List<Apple> apples = new ArrayList<Apple>();
	static List<Fruit> fruit = new ArrayList<Fruit>();

	static <T> void writeExact(List<T> list, T item) {
		list.add(item);
	}
	static void f1() {
		writeExact(apples, new Apple());//不使用通配符 向AppleList添加Apple
		// writeExact(fruit, new Apple()); // Error:
		// Incompatible types: found Fruit, required Apple
		//不使用通配符 无法向FruitList添加Apple 即使知道可以
	}

	static <T> void writeWithWildcard(List<? super T> list, T item) {//使用通配符
		//?是T的父类
		
		list.add(item);
	}

	static void f2() {
		writeWithWildcard(apples, new Apple());
		writeWithWildcard(fruit, new Apple());//使用通配符才可以向fruitList添加Apple
		//调用时fruit是下界 因此可以向该list添加fruit或者其子类的对象
	}

	public static void main(String[] args) {
		f1();
		f2();
	}
} // /:~

//使用上界通配符读取对象

public class GenericReading {
	static <T> T readExact(List<T> list) {
		return list.get(0);
	}

	static List<Apple> apples = Arrays.asList(new Apple());
	static List<Fruit> fruit = Arrays.asList(new Fruit());

	// A static method adapts to each call:
	static void f1() {
		Apple a = readExact(apples);//返回一个Apple
		Fruit f = readExact(fruit);//返回一个Fruit
		f = readExact(apples);//返回一个Apple赋值给Fruit
	}

	// If, however, you have a class, then its type is
	// established when the class is instantiated:
	static class Reader<T> {
		T readExact(List<T> list) {
			return list.get(0);
		}
	}

	static void f2() {
		Reader<Fruit> fruitReader = new Reader<Fruit>();//泛型确定为Fruit
		Fruit f = fruitReader.readExact(fruit);
		// Fruit a = fruitReader.readExact(apples); // Error:
		// readExact(List<Fruit>) cannot be
		// applied to (List<Apple>).
		// 如15.10所述 泛型不支持协变
	}

	static class CovariantReader<T> {
		T readCovariant(List<? extends T> list) {//使用上界通配符来读取
			return list.get(0);
		}
	}

	static void f3() {
		CovariantReader<Fruit> fruitReader = new CovariantReader<Fruit>();
		//可以从fruit列表读取fruit或者apples列表读取apple赋值给Fruit
		Fruit f = fruitReader.readCovariant(fruit);
		Fruit a = fruitReader.readCovariant(apples);
	}

	public static void main(String[] args) {
		f1();
		f2();
		f3();
	}
} // /:~

上面两个例子分别显示了上界通配符和下界通配符的使用场景


15.10.3 无界通配符

<?> 看起来意味着任何事物 那么它和Object有什么区别呢 其实有区别的
//本示例综合使用了上界 下界 无界通配符

public class Wildcards {
	// Raw argument:
	static void rawArgs(Holder holder, Object arg) {
		 holder.set(arg); // Warning:
		// Unchecked call to set(T) as a
		// member of the raw type Holder
		// holder.set(new Wildcards()); // Same warning

		// Can't do this; don't have any 'T':
		// T t = holder.get();

		// OK, but type information has been lost:
		Object obj = holder.get();
	}

	// Holder<?> 和 Holder<Object>的区别
	// Holder<Object>是持有任何类型的数组
	// Holder<?> 是持有某种类型的同种类型的集合
	static void unboundedArg(Holder<?> holder, Object arg) {
		// holder.set(arg); // Holder<?> 是持有某种类型的同种类型的集合 不能只向其中放入Object
		// set(capture of ?) in Holder<capture of ?>
		// cannot be applied to (Object)
		// holder.set(new Wildcards()); // Same error

		// Can't do this; don't have any 'T':
		// T t = holder.get();

		// OK, but type information has been lost:
		Object obj = holder.get();
	}

	static <T> T exact1(Holder<T> holder) {
		T t = holder.get();
		return t;
	}

	static <T> T exact2(Holder<T> holder, T arg) {
		holder.set(arg);
		T t = holder.get();
		return t;
	}

	//子类(上界)通配符 适用于get
	static <T> T wildSubtype(Holder<? extends T> holder, T arg) {
		// holder.set(arg); // Error:
		// set(capture of ? extends T) in
		// Holder<capture of ? extends T>
		// cannot be applied to (T)
		T t = holder.get();
		return t;
	}

	//父类(下界)通配符 适用于set
	static <T> void wildSupertype(Holder<? super T> holder, T arg) {
		holder.set(arg);
		// T t = holder.get(); // Error:
		// Incompatible types: found Object, required T

		// OK, but type information has been lost:
		Object obj = holder.get();
	}

	public static void main(String[] args) {
		//再次比较<?>和原生类型<Object>的区别
		ArrayList<?> arrays = new ArrayList<>();
		//arrays.add(new Object());//报错
		ArrayList<Object> arrays1 = new ArrayList<>();
		arrays1.add(new Object());
		
		Holder raw = new Holder<Long>();
		// Or:
		raw = new Holder();
		Holder<Long> qualified = new Holder<Long>();
		Holder<?> unbounded = new Holder<Long>();
		Holder<? extends Long> bounded = new Holder<Long>();
		Long lng = 1L;

		rawArgs(raw, lng);
		rawArgs(qualified, lng);
		rawArgs(unbounded, lng);
		rawArgs(bounded, lng);

		unboundedArg(raw, lng);
		unboundedArg(qualified, lng);
		unboundedArg(unbounded, lng);
		unboundedArg(bounded, lng);

		// Object r1 = exact1(raw); // Warnings:
		// Unchecked conversion from Holder to Holder<T>
		// Unchecked method invocation: exact1(Holder<T>)
		// is applied to (Holder)
		Long r2 = exact1(qualified);
		Object r3 = exact1(unbounded); // Must return Object
		Long r4 = exact1(bounded);

		// Long r5 = exact2(raw, lng); // Warnings:
		// Unchecked conversion from Holder to Holder<Long>
		// Unchecked method invocation: exact2(Holder<T>,T)
		// is applied to (Holder,Long)
		Long r6 = exact2(qualified, lng);
		// Long r7 = exact2(unbounded, lng); // Error:
		// exact2(Holder<T>,T) cannot be applied to
		// (Holder<capture of ?>,Long)
		// Long r8 = exact2(bounded, lng); // Error:
		// exact2(Holder<T>,T) cannot be applied
		// to (Holder<capture of ? extends Long>,Long)

		// Long r9 = wildSubtype(raw, lng); // Warnings:
		// Unchecked conversion from Holder
		// to Holder<? extends Long>
		// Unchecked method invocation:
		// wildSubtype(Holder<? extends T>,T) is
		// applied to (Holder,Long)
		Long r10 = wildSubtype(qualified, lng);
		// OK, but can only return Object:
		Object r11 = wildSubtype(unbounded, lng);
		Long r12 = wildSubtype(bounded, lng);

		// wildSupertype(raw, lng); // Warnings:
		// Unchecked conversion from Holder
		// to Holder<? super Long>
		// Unchecked method invocation:
		// wildSupertype(Holder<? super T>,T)
		// is applied to (Holder,Long)
		wildSupertype(qualified, lng);
		// wildSupertype(unbounded, lng); // Error:
		// wildSupertype(Holder<? super T>,T) cannot be
		// applied to (Holder<capture of ?>,Long)
		// wildSupertype(bounded, lng); // Error:
		// wildSupertype(Holder<? super T>,T) cannot be
		// applied to (Holder<capture of ? extends Long>,Long)
	}
} // /:~

15.10.4 捕获转换

//f2使用了无界通配符 但它调用f1时 f1仍然可以知道具体类型
public class CaptureConversion {
	static <T> void f1(Holder<T> holder) {//该方法没有使用通配符 因此没有边界
		T t = holder.get();
		System.out.println(t.getClass().getSimpleName());
	}

	static void f2(Holder<?> holder) {//f2使用了无界通配符 并且调用了没有使用通配符的方法f1
		f1(holder); // Call with captured type
	}

	@SuppressWarnings("unchecked")
	public static void main(String[] args) {
		Holder raw = new Holder<Integer>(1);
		f1(raw); // Produces warnings
		f2(raw); // No warnings
		Holder rawBasic = new Holder();
		rawBasic.set(new Object()); // Warning
		f2(rawBasic); // No warnings
		// Upcast to Holder<?>, still figures it out:
		Holder<?> wildcarded = new Holder<Double>(1.0);
		f2(wildcarded);
	}
} /*
 * Output: Integer Object Double
 */// :~

15.11 问题

泛型的各种问题

15.11.1 任何基本类型都不能作为类型参数

由于基本类型都有包装类 因此 大部分问题都可以解决 比如像下面这个

//使用包装类创建泛型集合类
public class ByteSet {
	Byte[] possibles = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	Set<Byte> mySet = new HashSet<Byte>(Arrays.asList(possibles));
	// But you can't do this:
	// Set<Byte> mySet2 = new HashSet<Byte>(
	// Arrays.<Byte>asList(1,2,3,4,5,6,7,8,9));
} // /:~

//包装类无法解决基本类型无法作为泛型类型的所有问题
// Fill an array using a generator:
class FArray {
	public static <T> T[] fill(T[] a, Generator<T> gen) {//Generator是一个接口 仅有一个next方法
		//方法作用为填充数组并返回
		for (int i = 0; i < a.length; i++){
			a[i] = gen.next();
		}
		return a;
	}
}

public class PrimitiveGenericTest {
	public static void main(String[] args) {
		//RandomGenerator的作用是生成各种包装类的生成器 第十六章会讲
		String[] strings = FArray.fill(new String[7],
				new RandomGenerator.String(10));
		for (String s : strings)
			System.out.println(s);
		Integer[] integers = FArray.fill(new Integer[7],
				new RandomGenerator.Integer());
		for (int i : integers)
			System.out.println(i);
		// Autoboxing won't save you here. This won't compile:
		// int[] b =
		// FArray.fill(new int[7], new RandIntGenerator());
		// 看上去还是泛型数组无法直接赋值给基本类型数组
	}
} /*
 * Output: YNzbrnyGcF OWZnTcQrGs eGZMmJMRoE suEcUOneOE dLsmwHLGEa hKcxrEqUCB
 * bkInaMesbt 7052 6665 2654 3909 5202 2209 5458
 */// :~

15.11.2 实现参数化接口 一个类不能实现泛型接口的两种变体

一个类不能实现泛型接口的两种变体
比如下面这个例子 看Hourly类 由于父类实现了Payable ,因此Hourly类实现了Payable和Payable两个接口

interface Payable<T> {}

class Employee implements Payable<Employee> {}
class Hourly extends Employee
  implements Payable<Hourly> {} ///:~
//报错
//The interface Payable cannot be implemented more than once with different arguments: Payable<Employee> and Payable
//也就是说编译器认为Payable<Employee>和Payable<Hourly>是相同接口
//原因是类型擦除

15.11.3 泛型转型和警告

我们必须在一些情况加上@SuppressWarnings(“unchecked”) 但是按道理讲是没有必要的 (泛型强制转换看起来无效)

//加了泛型 仍然需要转型
public class NeedCasting {
	@SuppressWarnings("unchecked")
	public void f(String[] args) throws Exception {
		ObjectInputStream in = new ObjectInputStream(new FileInputStream(
				args[0]));
		//泛型转型似乎没有效果 仍然提示需要转型
		List<Widget> shapes = (List<Widget>) in.readObject();
		//不使用泛型 则不会发出警告
		//List shapes = (ArrayList) in.readObject();
	}
} // /:~
/**
 如果去掉@SuppressWarnings("unchecked")
则出现下面的情况
C:\Users\hjcai\Desktop>javac NeedCasting.java
Note: NeedCasting.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

C:\Users\hjcai\Desktop>javac -Xlint:unchecked NeedCasting.java
NeedCasting.java:13: warning: [unchecked] unchecked cast
                List<Widget> shapes = (List<Widget>) in.readObject();
                                                                  ^
  required: List<Widget>
  found:    Object
1 warning

看起来(List<Widget>)不是一个强制转换 
 **/

看另一个例子 这里我们使用Java SE5新的转型方式 – 泛型类转型

public class ClassCasting {
	@SuppressWarnings("unchecked")
	public void f(String[] args) throws Exception {
		ObjectInputStream in = new ObjectInputStream(new FileInputStream(
				args[0]));
		// Won't Compile:
		// List<Widget> lw1 =
		// List<Widget>.class.cast(in.readObject());
		List<Widget> lw2 = List.class.cast(in.readObject());
		List<Widget> lw3 = (List<Widget>) List.class.cast(in.readObject());//不加@SuppressWarnings和lw2一样的警告
	}
} // /:~

/**
 * 当我们去掉@SuppressWarnings("unchecked")时 仍然有警告
C:\Users\hjcai\Desktop>javac -Xlint:unchecked ClassCasting.java
ClassCasting.java:13: warning: [unchecked] unchecked conversion
    List<Widget> lw2 = List.class.cast(in.readObject());
                                      ^
  required: List<Widget>
  found:    List
ClassCasting.java:15: warning: [unchecked] unchecked cast
  (List<Widget>)List.class.cast(in.readObject());
                               ^
  required: List<Widget>
  found:    List
2 warnings
 **/

15.11.4 存在泛型参数方法的重载

// {CompileTimeError} (Won't compile)
import java.util.*;

public class UseList<W, T> {

	//由于类型擦除 这两个方法在编译器看来是同一个方法 因此无法编译
	void f(List<T> v) {
	}

	void f(List<W> v) {
	}
} // /:~

public class UseList2<W,T> {
	//必须使用不同的方法名以示区别才可以编译
  void f1(List<T> v) {}
  void f2(List<W> v) {}
} ///:~

15.11.5 基类劫持接口 (基类实现接口时确定了泛型类型 子类无法修改)

//ComparablePet实现Comparable接口 期望可以进行比较
public class ComparablePet implements Comparable<ComparablePet> {
	public int compareTo(ComparablePet arg) {
		return 0;
	}
} // /:~

// {CompileTimeError} (Won't compile)

//Cat继承了ComparablePet并实现Comparable接口 期望对Comparable进行窄化处理
//Cat只能与Cat比较
//但是无法编译  报错如下
//The interface Comparable cannot be implemented more than once with different arguments: Comparable<ComparablePet> and Comparable<Cat>
//由于擦除 父类子类的实现接口相同 这里如果不使用泛型 可以编译,使用泛型导致报错
//因为Comparable的参数类型在父类已经确定 子类无法修改类型
class Cat extends ComparablePet implements Comparable<Cat>{
  // Error: Comparable cannot be inherited with
  // different arguments: <Cat> and <Pet>
  public int compareTo(Cat arg) { return 0; }
} ///:~

//要想覆盖基类的Comparable接口
//只能确保类型完全相同

//方式1
class Hamster extends ComparablePet implements Comparable<ComparablePet> {
	public int compareTo(ComparablePet arg) {
		return 0;
	}
}

//方式二
// Or just:
class Gecko extends ComparablePet {
	public int compareTo(ComparablePet arg) {
		return 0;
	}
} // /:~

15.12 自限定的类型

我们也许会看到类似
class MyTest<T extends MyTest>{

}
之类的声明,这就是循环泛型,这看起来很难理解,让我们从简单的入手

15.12.1 古怪的循环泛型

先不使用自限定边界(不使用extends泛型边界)

public class BasicHolder<T> {
	T element;

	void set(T arg) {
		element = arg;
	}

	T get() {
		return element;
	}

	void f() {
		System.out.println(element.getClass().getSimpleName());
	}
} // /:~

//Subtype类继承一个泛型类型 该类型接受Subtype作为参数
class Subtype extends BasicHolder<Subtype> {
}

public class CRGWithBasicHolder {
	public static void main(String[] args) {
		Subtype st1 = new Subtype(), st2 = new Subtype();
		st1.set(st2);
		Subtype st3 = st1.get();
		st1.f();
	}
} /*
 * Output: Subtype
 */// :~
/**
子类Subtype接受的参数以及返回值均是Subtype类型 而不是父类类型
CRG(循环泛型)的本质:基类中的泛型类型用子类类型代替
也就是说 泛型基类变成所有子类公共功能的模板 这些方法对于所有参数和返回值将使用子类类型
比如该例子 set的参数和get的返回值均是子类类型
 */

这里的BasicHolder 看起来变成了所有子类的一个公共模板


15.12.2 自限定

上面的BasicHolder可以使用仍以类型作为其泛型参数 比如:

class Other {
}

class BasicOther extends BasicHolder<Other> {
}

public class Unconstrained {
	public static void main(String[] args) {
		BasicOther b = new BasicOther(), b2 = new BasicOther();
		b.set(new Other());
		Other other = b.get();
		b.f();
	}
} /*
 * Output: Other
 */// :~

上面这个例子和Subtype几乎一样


下面我们更进一步 使用泛型边界限定(extends) 观察具体的使用方法 以及哪些不可以使用

class SelfBounded<T extends SelfBounded<T>> {//基类使用自限定泛型类型
	//可以对比BasicHolder 容易理解
	T element;

	SelfBounded<T> set(T arg) {//注意返回值 返回的是泛型类型T
		element = arg;
		return this;
	}

	T get() {
		return element;
	}
}

class A extends SelfBounded<A> {//强制要求A类传递给基类 使用A类当作泛型类型
}

class B extends SelfBounded<A> {//虽然可以这么写 但是很少这么用
	//由于A extends SelfBounded<A> 因此
	//A类满足<T extends SelfBounded<T>>
} // Also OK

class C extends SelfBounded<C> {
	C setAndGet(C arg) {//新增方法 参数和返回值都是确切类型(C)
		set(arg);
		return get();
	}
}

class D {
}

// Can't do this:
// class E extends SelfBounded<D> {}
// Compile error: Type parameter D is not within its bound
//编译错误 参数类型D不在边界内

// Alas, you can do this, so you can't force the idiom:
//但是你却可以这么做
class F extends SelfBounded {
}

public class SelfBounding {
	public static void main(String[] args) {
		A a = new A();
		a.set(new A());
		a = a.set(new A()).get();
		a = a.get();
		C c = new C();
		c = c.setAndGet(new C());
	}
} // /:~
//自限定的参数意义在于 确保类型参数与正在被定义的类相同
------

再对比一下没有使用自限定限制

//不使用自限定泛型
public class NotSelfBounded<T> {//该类和BasicHolder基本一致
	T element;

	NotSelfBounded<T> set(T arg) {
		element = arg;
		return this;
	}

	T get() {
		return element;
	}
}

class A2 extends NotSelfBounded<A2> {
}

class B2 extends NotSelfBounded<A2> {
}

class C2 extends NotSelfBounded<C2> {
	C2 setAndGet(C2 arg) {
		set(arg);
		return get();
	}
}

class D2 {
}

// Now this is OK:
class E2 extends NotSelfBounded<D2> {//自限定限制只能强制作用于继承关系 
} // /:~

15.12.3 参数协变(考虑什么情况可以进行基于参数类型的重载)

不使用自限定泛型例子

class Base {
}

class Derived extends Base {
}

interface OrdinaryGetter {
	Base get();
}

interface DerivedGetter extends OrdinaryGetter {
	// Return type of overridden method is allowed to vary:
	//返回类型允许修改(修改为范围更小的类型)
	@Override
	Derived get();
}

public class CovariantReturnTypes {
	void test(DerivedGetter d) {
		Derived d2 = d.get();
	}
} // /:~

使用自限定类型

//作用和CovariantReturnTypes一样 关注返回类型
interface GenericGetter<T extends GenericGetter<T>> {
	T get();
}

interface Getter extends GenericGetter<Getter> {
}

public class GenericsAndReturnTypes {
	void test(Getter g) {
		Getter result = g.get();
		GenericGetter gg = g.get(); // Also the base type
	}
} // /:~

不使用自限定泛型 关注点转移到参数类型

class OrdinarySetter {
	void set(Base base) {
		System.out.println("OrdinarySetter.set(Base)");
	}
}

class DerivedSetter extends OrdinarySetter {//DerivedSetter存在两个set方法
	//对比DerivedGetter 返回值可以修改 但是参数不可以修改 这里不是覆盖
	void set(Derived derived) {
		System.out.println("DerivedSetter.set(Derived)");
	}
}

public class OrdinaryArguments {
	public static void main(String[] args) {
		Base base = new Base();
		Derived derived = new Derived();
		DerivedSetter ds = new DerivedSetter();
		ds.set(derived);
		//可以编译 但是是重载 不是覆盖
		ds.set(base); // Compiles: overloaded, not overridden!
	}
} /*
 * Output: DerivedSetter.set(Derived) OrdinarySetter.set(Base)
 */// :~

//使用自限定泛型 关注参数类型
interface SelfBoundSetter<T extends SelfBoundSetter<T>> {
	void set(T arg);
}

interface Setter extends SelfBoundSetter<Setter> {//子类只有一个set方法 并且参数类型是子类类型
}

public class SelfBoundingAndCovariantArguments {
	void testA(Setter s1, Setter s2, SelfBoundSetter sbs) {
		s1.set(s2);
		// s1.set(sbs); // Error:不能使用父类类型
		// set(Setter) in SelfBoundSetter<Setter>
		// cannot be applied to (SelfBoundSetter)
	}
} // /:~

不使用自限定泛型

//对比OrdinaryArguments 关注参数类型
class GenericSetter<T> { // Not self-bounded
	void set(T arg) {
		System.out.println("GenericSetter.set(Base)");
	}
}

class DerivedGS extends GenericSetter<Base> {
	//没有使用自限定类型 将可以进行重载
	void set(Derived derived) {
		System.out.println("DerivedGS.set(Derived)");
	}
}

public class PlainGenericInheritance {
	public static void main(String[] args) {
		Base base = new Base();
		Derived derived = new Derived();
		DerivedGS dgs = new DerivedGS();
		dgs.set(derived);
		dgs.set(base); // Compiles: overloaded, not overridden!
	}
} /*
 * Output: DerivedGS.set(Derived) GenericSetter.set(Base)
 */// :~

结论:使用自限定类型 子类将使用确切的子类类型,如果不使用自限定类型,方法可以被重载(参数类型方式重载)


15.13 动态类型安全

由于JavaSE5之前不支持泛型 那么旧代码可能破坏你的泛型容器 尝试向其中添加类型不正确的对象 java.util.Collections中存在一系列check方法可以解决这类类型错误问题
如果不使用这类方法 泛型类型的容器将会在取出对象时报错

// Using Collection.checkedList().
import java.util.*;

class Pet {

}

class Dog extends Pet {

}

class Cat extends Pet {

}

public class CheckedList {
	@SuppressWarnings("unchecked")
	static void oldStyleMethod(List probablyDogs) {
		probablyDogs.add(new Cat());// 悄悄地在DogList插入一个Cat
	}

	public static void main(String[] args) {
		//创建Dog list
		List<Dog> dogs1 = new ArrayList<Dog>();
		oldStyleMethod(dogs1); // 悄悄地插入一个Cat
		//参数:检查的列表 列表中的类型
		List<Dog> dogs2 = Collections.checkedList(new ArrayList<Dog>(),
				Dog.class);
		try {
			oldStyleMethod(dogs2); // Throws an exception
		} catch (Exception e) {
			System.out.println(e);
		}
		// Derived types work fine:
		// 基类类型列表正常工作
		List<Pet> pets = Collections.checkedList(new ArrayList<Pet>(),
				Pet.class);
		pets.add(new Dog());
		pets.add(new Cat());
	}
} /*
 * Output: java.lang.ClassCastException: Attempt to insert class
 * typeinfo.pets.Cat element into collection with element type class
 * typeinfo.pets.Dog
 */// :~

15.14 异常

将泛型应用于异常是受限的,因为
1.由于擦除,在运行时catch语句不能知晓异常的确切类型
2.泛型类不能继承Throwable(The generic class XXX may not subclass java.lang.Throwable)
但是可以以另外一种形式引入异常

interface Processor<T, E extends Exception> {//以第二个参数的形式将Exception引入
	void process(List<T> resultCollector) throws E;
}

class ProcessRunner<T, E extends Exception> extends ArrayList<Processor<T, E>> {
	List<T> processAll() throws E {
		List<T> resultCollector = new ArrayList<T>();
		for (Processor<T, E> processor : this)
			processor.process(resultCollector);
		return resultCollector;
	}
}

class Failure1 extends Exception {
}

class Processor1 implements Processor<String, Failure1> {
	static int count = 3;//注意是static的 所有对象公用

	public void process(List<String> resultCollector) throws Failure1 {
		//初始值为3
		//第一次3 > 1add("Hep!")
		//第二次2 > 1add("Hep!")
		//第三次1不大于1add("Ho!")
		//且没有抛出异常
		//如果把count初始值改为小于等于1的值 将会抛出异常
		if (count-- > 1)
			resultCollector.add("Hep!");
		else
			resultCollector.add("Ho!");
		if (count < 0)
			throw new Failure1();
	}
}

class Failure2 extends Exception {
}

class Processor2 implements Processor<Integer, Failure2> {
	static int count = 2;

	public void process(List<Integer> resultCollector) throws Failure2 {
		//
		if (count-- == 0)
			resultCollector.add(47);
		else {
			resultCollector.add(11);
		}
		if (count < 0)
			throw new Failure2();
	}
}

public class ThrowGenericException {
	public static void main(String[] args) {
		ProcessRunner<String, Failure1> runner = new ProcessRunner<String, Failure1>();
		for (int i = 0; i < 3; i++)
			runner.add(new Processor1());//执行3次
		try {
			System.out.println(runner.processAll());
		} catch (Failure1 e) {
			System.out.println(e);
		}

		ProcessRunner<Integer, Failure2> runner2 = new ProcessRunner<Integer, Failure2>();
		for (int i = 0; i < 3; i++)
			runner2.add(new Processor2());//执行3次
		try {
			System.out.println(runner2.processAll());
		} catch (Failure2 e) {
			System.out.println(e);
		}
	}
} // /:~
/**
[Hep!, Hep!, Ho!]
generics.Failure2
 */

15.15 混型

这里的混型是指混合多个类的能力到一个类上
15.15.1& 15.15.2
由于JAVA不支持多重继承 只能用接口来模拟这种情况
C++使用混型比Java容易得多,原因在于它支持多重继承 Java要实现混型 其代码量远大于C++
15.15.3
书中提到Java的混型与装饰器模式很类似,这里不是很理解。装饰器模式在于装饰者和被装饰者都继承了同一个基类
而混型的关键在于多重继承。也许是理解还不够到位,看不出来相似性。
虽然说书中将混型的例子用装饰器模式实现了,但是其实混型和装饰器还是有很大不同的。关于装饰器可以参考我之前的链接
https://blog.csdn.net/u011109881/article/details/81051385
书中接下来将例子修改为使用装饰器模式实现 但是感觉和真正的混型差别很大。
15.15.4 与动态代理混合
有些看不明白。。。


15.16 潜在类型机制

所谓潜在类型机制是指一种类型 只要该类型满足具有某些特定方法 就属于一种特殊类型(和实现了某个接口的类很类似的概念)
在C++与Python中 这种实现很灵活,而在Java中 最终还是回归到实现特定接口上了

public interface Performs {
  void speak();
  void sit();
} ///:~

class PerformingDog extends Dog implements Performs {
	public void speak() {
		print("Woof!");
	}

	public void sit() {
		print("Sitting");
	}

	public void reproduce() {
	}
}

class Robot implements Performs {
	public void speak() {
		print("Click!");
	}

	public void sit() {
		print("Clank!");
	}

	public void oilChange() {
	}
}

//不明白这么写的目的 感觉多此一举 因为下面那种实现更清晰,可能是为了模仿C++和python?
//class Communicate {
//	public static <T extends Performs> void perform(T performer) {
//		performer.speak();
//		performer.sit();
//	}
//}
class Communicate {
	public static void perform(Performs performer) {
		performer.speak();
		performer.sit();
	}
}

public class DogsAndRobots {
	public static void main(String[] args) {
		PerformingDog d = new PerformingDog();
		Robot r = new Robot();
		Communicate.perform(d);
		Communicate.perform(r);
	}
} /*
 * Output: Woof! Sitting Click! Clank!
 */// :~

15.17 15.18 都在强调如何在Java中实现潜在类型机制以及如何优化。 但实际中 潜在类型机制似乎并没有广泛的使用,这部分我本身也看得云里雾里 就先跳过了。


15.19 总结

即使没有泛型 Java的强制转换其实不是很遭,泛型的最根本是解决将Dog类型的对象插入到Cat列表,但其实这类问题很容易发现,但是,因为泛型是java诞生很久之后添加的新功能,其兼容性使得程序的升级变得相当复杂,而且,本章也讨论了泛型的各种缺陷,因此引入泛型究竟是好是坏,这个问题值得深思。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值