《java編程思想——第十五章(泛型)》

泛型

15.2 簡單泛型##

泛型的主要目的之一就是用來指定容器要持有什麼類型的對象,而且由編譯器來來保證正確性。

// 類型參數T
public class Holder3<T> {
	private T t;
	public Holder3(T a) {
		this.t = a;
	}
	public void set(T a){
		this.t = a;
	}
	public T get() {
		return t;
	}
	public static void main(String[] args) {
		Holder3<Automobile> h2 =  new Holder3<Automobile>(new Automobile());
		Automobile a = (Automobile) h2.get();

	}
}

使用類型參數T可以暫時不指定類型,在創建對象時必須指明類型。

  1. 一個元組類庫
    將一組對象打包存儲咋一個對象中稱爲元組。
/**
 * 元組持有數據
 * @author Administrator
 *
 */

public class TwoTuple<A,B> {

	public final A first;
	public final B second;
	public TwoTuple(A a,B b){
		first = a;
		second = b;
	}
	@Override
	public String toString() {
		return "("+first+","+second+")";
	}
}

可以通過元組,返回一組對象。
2. 一個堆棧類

 // 自定義堆棧
public class LinkedStack<T> {

	private static class Node<U> {
		U item;
		Node<U> next;
		Node() {
			item = null;
			next = null;
		}
		Node(U item,Node<U> next){
			this.item = item;
			this.next = next;
		}
		boolean end(){
			return item == null && next == null;
		}
	}
	private Node<T> top = new Node<T>();
	
	public void push(T item){
		top = new Node<T>(item, top);
	}
	public T pop(){
		T result = top.item;
		if(!top.end()){
			top = top.next;
		}
		return result;
	}
	
	public static void main(String[] args) {
		LinkedStack<String> s = new LinkedStack<String>();
		for (String string : "Phasers on stun!".split("")) {
			s.push(string);
		}
		
		Print.print(s.pop());
	}
}

使用了末端哨兵來判斷堆棧是否爲空。
3. RandomList

15.3 泛型接口##

泛型也可用於接口。

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

泛型接口應用:
生成器利用工程模式生成對象。
利用適配器模式可以實現迭代功能。

15.4 泛型方法##

使用泛型了時,在創建對象時必須指明類型參數;使用泛型方法時,不必指明參數類型。編譯器會找出具體的類型,被稱爲參數推斷

定義泛型方法,只需將泛型參數列表置於返回值之前。

//泛型方法
public <T>  void f(T t) {
	System.out.println(t.getClass().getName());
}
  1. 槓桿利用參數推斷
    類型推斷只對賦值有效,作爲參數傳遞無效。
  2. 可變參數與泛型方法
//可變參數與泛型方法可以很好的共存
public class GenericVarargs {
	public static <T> List<T> makeList(T... args) {
		List<T> result =  new ArrayList<T>();
		for (T t : args) {
			result.add(t);
		}
		return result;
	}
	public static void main(String[] args) {
		List<String> list = makeList("A");
		System.out.println(list);
		list = makeList("A","B","C");
		System.out.println(list);
	}
}

15.5 擦除的神祕之處##

class Frob{}
class Fnorkle{}
class Quark<Q>{}
class Practice<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>();
		Practice<Long,Double> practice = new Practice<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(practice.getClass().getTypeParameters()));

	}
}
輸出:
[E]
[K, V]
[Q]
[POSITION, MOMENTUM]

在泛型代碼內部,無法獲得任何有關泛型參類型的信息。

擦除的代價是:泛型不能顯式地應用運行時類型操作。因爲所有參數 類型信息都丟失了。

15.5 擦除的補償##

泛型擦除無法在運行時獲取類型信息,可以通過傳入類型解決。

/**
 * 通過傳入Class獲取類型
 * @author Administrator
 */
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<House> ct2 = 
				new ClassTypeCapture<House>(House.class);
		System.out.println(ct2.f(new Building()));
		System.out.println(ct2.f(new House()));
	}
}

泛型擦除創建對象方式:工廠方式和模板方式。
不能創建泛型數組解決方式:使用ArrayList。

15.6 邊界##

邊界使得你可以在用於泛型的參數類型上設置條件。

<T extends 接口名或類名>

15.7 通配符##

< ? extends 接口名或類名>
任何從某個類繼承的類型都適用。
< ? super 接口名或類名> 超類型通配符
無界通配符:<?>

15.8 問題##

  1. 任何基本數據類型都不能作爲類型參數
  2. 實現參數化接口
    一個類不能實現同一個泛型接口的兩種變體。
  3. 轉型和警告
  4. 重載
    由於泛型擦除的原因,重載方法將產生相同簽名。
  5. 基類劫持了接口

15.9 自限定類型##

ClassA extens ClassB<A>

自限定會強制要求將正在定義的類當做參數傳遞給基類。

15.10 動態類型安全##

1.5以後可以使用Collections工具類中的checkedList(list, type),checkedMap()等方法檢查傳入容器的類型。

15.11 異常##

類型參數可應用於一個方法的throws子句中。

15.12 混型##

混型:混個多個類的能力,產生一個可以表示混型中所有類型的類。

  1. 與接口混合
    使用代理方式,每個混入類型中都有相應的域,使用時代理調用方法。
  2. 裝飾器模式
  3. 與動態代理結合

15.13 潛在類型機制##

class Dog:
	def speak(self):
		print "atf:
	def sit(self):
		print "Sitting"
	def reproduce(self):
		pass
class Robot:
	def speak(self):
		print "atf:
	def sit(self):
		print "Sitting"
	def reproduce(self):
		pass
a = Dog()
b = Robot()
perform(a)
perform(b)

perform(anything) 沒有針對任何anything類型,anything包含的接口是潛在的。
Java不支持潛在類型機制。

15.17 對缺乏潛在類型機制的補償##

  1. 反射
    利用反射可以動態地確定所需要的方法並調用它。
  2. 將方法應用於序列
    反射提供了一些有趣的可能,但它將所有的類型檢查都轉移到了運行時。
    嘗試一些實現編譯時期檢查。
  3. 當你並未擁有正確接口時
    編譯時期可以檢查類型,但是類型被限制在繼承層次之內。
  4. 用適配器仿真潛在類型
    適配器模式模仿潛在類型機制,並在編譯時期檢查類型。
/**
 * 適配器模式實現潛在類型機制
 * @author Administrator
 *
 * @param <T>
 */
interface Addable<T> { void add(T t);}
class AddableCollectionAdapter<T> implements Addable<T>{
	private Collection<T> c;
	public AddableCollectionAdapter(Collection<T> c) {
		this.c = c;
	}
	
	@Override
	public void add(T t) {
		c.add(t);
	}
	
}
class Adapter{
	public static<T>  Addable<T> collectionAdapter(Collection<T> c) {
		return new AddableCollectionAdapter(c);
	}
}
class AddableSimpleQueue<T> extends SimpleQueue<T> implements Addable<T>{
	public void add(T t) {
		super.add(t);
	}
}
public class Fill2 {
	public static<T> void fill(Addable<T> addable,
			Class<? extends T> classToken,int size) {
		for (int i = 0; i < size; i++) {
			try {
				addable.add(classToken.newInstance());
			} catch (Exception e) {
				throw new RuntimeException();
			}
		}
	}
	public static<T> void fill(Addable<T> addable,
			Generator< T> generator,int size) {
		for (int i = 0; i < size; i++) {
			try {
				addable.add(generator.next());
			} catch (Exception e) {
				throw new RuntimeException();
			}
		}
	}
	
	public static void main(String[] args) {
		List<Coffee> coffees = new ArrayList<>();
		Fill2.fill(new AddableCollectionAdapter<Coffee>(coffees), Coffee.class, 3);
		Fill2.fill(Adapter.collectionAdapter(coffees), Coffee.class, 3);
		for (Coffee coffee : coffees) {
			System.out.println(coffee);
		}
		System.out.println("--------------------");
		AddableSimpleQueue<Coffee> coffeequeue = new AddableSimpleQueue();
		Fill2.fill(coffeequeue, Mocha.class, 5);
		Fill2.fill(coffeequeue, Latte.class, 6);
		for (Coffee coffee : coffeequeue) {
			System.out.println(coffee);
		}
	}
}

15.18 將函數對象用作策略##

策略設計模式將變化的事物隔離到函數對象中。

15.19 總結##

泛型的作用除了提供類型檢查,還可以編寫更"泛化"的代碼。

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