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誕生很久之後添加的新功能,其兼容性使得程序的升級變得相當複雜,而且,本章也討論了泛型的各種缺陷,因此引入泛型究竟是好是壞,這個問題值得深思。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章