1. 程式人生 > >java基礎總結 --- 泛型 擦除、邊界、萬用字元、

java基礎總結 --- 泛型 擦除、邊界、萬用字元、


* 擦除的問題
 * 為什麼要擦除: 1.5版本才出現泛型 為了相容之前地程式碼
 * 它使得泛化的客戶端可以用非泛化的類庫來使用。
 * 以及不破壞現有類庫的情況下,將泛型融入java語言。
 * 擦除使得現有的非泛型客戶端程式碼能夠在不改變的情況繼續使用,直至客戶端準備號用泛型重寫這些程式碼。
 * 擦除的代價:泛型不能用於顯示地引用執行時型別的操作值中,例如轉型instanceof操作和new表示式。
 * 因為所有關於引數的型別都丟失了。無論何時,當你在編寫泛型程式碼時,必須時刻提醒自己,
 * 你只是看起來好像擁有關於引數的型別資訊而已。提醒自己:T 不,它值時一個Object
 * 擦除使用泛型並不是強制的
package com.zghw.base.generic;

import java.util.*;

/**
 * 擦除的問題
 * 為什麼要擦除: 1.5版本才出現泛型 為了相容之前地程式碼
 * 它使得泛化的客戶端可以用非泛化的類庫來使用。
 * 以及不破壞現有類庫的情況下,將泛型融入java語言。
 * 擦除使得現有的非泛型客戶端程式碼能夠在不改變的情況繼續使用,直至客戶端準備號用泛型重寫這些程式碼。
 * 擦除的代價:泛型不能用於顯示地引用執行時型別的操作值中,例如轉型instanceof操作和new表示式。
 * 因為所有關於引數的型別都丟失了。無論何時,當你在編寫泛型程式碼時,必須時刻提醒自己,
 * 你只是看起來好像擁有關於引數的型別資訊而已。提醒自己:T 不,它值時一個Object
 * 擦除使用泛型並不是強制的
 * @author zghw
 *
 */
class Frob{}
class Fnorkle{}
class Quark<Q>{}
class Particle<POSITION,MOMENTUM>{}
public class ErasedTypeEquivalence {
	/**
	 * ArrayList<String>和ArrayList<Integer>很容易認為是不同的型別,
	 * 不同的型別在行為方面肯定不同 但輸出發現c1==c2為true
	 * @param args
	 */
	public static void main(String[] args) {
		Class c1=new ArrayList<String>().getClass();
		Class c2=new ArrayList<Integer>().getClass();
		System.out.println(c1==c2);//輸出為true
		
		List<Frob> list = new ArrayList<Frob>();
		Map<Frob,Fnorkle> map = new HashMap<Frob,Fnorkle>();
		Quark<Fnorkle> quark = new Quark<Fnorkle>();
		Particle<Long,Double> part=new Particle<Long,Double>();
		//Class.getTypeParameters()將返回一個Typeariable物件陣列,表示有泛型宣告
		//的型別引數...
		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(part.getClass().getTypeParameters()));
		/**
		 * 輸出結果:
		 * [E]
		   [K, V]
		   [Q]
		   [POSITION, MOMENTUM]
		 */
		/**
		 * 輸出結果是一些佔位符
		 * 說明:在泛型程式碼內部,無法獲得任何有關泛型引數型別的資訊。
		 * 當你在使用泛型時,任何具體的型別資訊都被擦除了,你唯一知道的就是你在使用一個物件。
		 * 因此,List<String>和List<Integer>在執行時事實上是相同的型別。這兩種形式都被
		 * 擦除成它們的原生型別,即List
		 */
	}

}

package com.zghw.base.generic;
/**
 * 擦除帶來的問題
 * 1.無法建立型別例項
 * 2.無法使用instanceof
 * 3.無法呼叫型別例項方法
 * 解決辦法
 * 通過引入型別標籤來對擦除進行補償。這意味著你需要顯示地傳遞你地型別地Class物件,
 * 以便你可以在型別表示式中使用它。
 * 可以使用?extends T 繼承來實現可以呼叫方法
 * @author zghw
 */
class Erased<T>{
	public static void f(Object arg){
		//if(arg instanceof T){}//Error無法使用instanceof
		//T var = new T();//Error無法建立型別例項
	}
}

class Building {}
class House extends Building{}
public class ClassTypeCapture<T> {
	Class<T> kind;
	T t ;
	public ClassTypeCapture(Class<T> kind){
		this.kind = kind;
		try {
			t=kind.newInstance();//建立型別例項
		} catch (Exception e) {
			throw new RuntimeException();
		}
	}
	
	public boolean isInstance(Object arg){
		return kind.isInstance(arg);
	}
	
	public static void main(String[] args) {
		ClassTypeCapture<Building> c1=new ClassTypeCapture<Building>(Building.class);
		ClassTypeCapture<House> c2=new ClassTypeCapture<House>(House.class);
		System.out.println(c1.isInstance(c2));
		System.out.println(c1.isInstance(new Building()));
		System.out.println(c1.isInstance(new House()));
		System.out.println(c2.isInstance(new Building()));
		System.out.println(c2.isInstance(new House()));
	}

}

package com.zghw.base.generic;

/**
 * 使用簡單工廠來建立泛型T 建議建立泛型物件使用此方法
 * 
 * @author zghw
 *
 */
interface Factory<T> {
	T creat();
}

class IntegerGen implements Factory<Integer> {

	@Override
	public Integer creat() {
		return new Integer(0);// 由於Integer沒有預設構造方法,無法使用Class建立泛型例項
	}

}

class Widget {
	public static class FactoryInner implements Factory<Widget> {

		@Override
		public Widget creat() {
			return new Widget();
		}

	}
}

class Foo<T> {
	private T x;

	public <F extends Factory<T>> Foo(F factory) {
		x = factory.creat();
	}

	public T get() {
		return x;
	}
}

public class FactoryConstraint {
	public static void main(String[] args) {
		Integer i = new Foo<Integer>(new IntegerGen()).get();
		Widget w = new Foo<Widget>(new Widget.FactoryInner()).get();

	}

}

package com.zghw.base.generic;

/**
 * 使用模板方法建立泛型例項
 * 
 * @author zghw
 *
 */
abstract class GenericWithCreator<T> {
	final T element;

	public GenericWithCreator() {
		element = creat();
	}

	abstract T creat();
}

class Obj {
}

class Creator extends GenericWithCreator<Obj> {
	@Override
	Obj creat() {
		return new Obj();
	}

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

public class CreatorGeneric {
	public static void main(String[] args) {
		new Creator().f();
	}

}

package com.zghw.base.generic;

import java.awt.Color;

/**
 * 邊界 邊界作用: 可以用於在泛型地引數上設定限制條件。
 * 你可以按照自己的邊界型別來呼叫方法。
 * 因為擦除移除了型別,所以你可以用泛型引數呼叫的方法只是Object呼叫的方法。
 * 但是,如果能夠將這個引數限制在某個型別子類,那麼你就可以用這些型別子集來呼叫方法。
 *  為了執行這種限制,Java泛型重用了extends關鍵字
 * 
 * @author zghw
 *
 */
interface HasColor {
	Color getColor();
}

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

interface Weight {
	int weight();
}

class HoldItem<T> {
	T item;

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

	T getItem() {
		return item;
	}
}

class Colored<T extends HasColor> {
	T item;

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

	Color getColor() {
		return item.getColor();
	}
}

class Colored2<T extends HasColor> extends HoldItem<T> {
	Colored2(T item) {
		super(item);
	}

	Color getColor() {
		return item.getColor();
	}
}

class ColoredDimension<T extends Dimension & HasColor> {
	T item;

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

	Color getColor() {
		return item.getColor();
	}

	int getX() {
		return item.x;
	}

	int getY() {
		return item.y;
	}

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

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();
	}
}

class Solid<T extends Dimension & HasColor & Weight> {
	T item;

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

	Color getColor() {
		return item.getColor();
	}

	int getX() {
		return item.x;
	}

	int getY() {
		return item.y;
	}

	int getZ() {
		return item.z;
	}

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

class Bounded extends Dimension implements HasColor, Weight {
	@Override
	public int weight() {
		return 0;
	}

	@Override
	public Color getColor() {
		return null;
	}

}

public class BasicBounds {
	public static void main(String args[]) {
		Solid<Bounded> s = new Solid<Bounded>(new Bounded());
		Solid2<Bounded> s2 = new Solid2<Bounded>(new Bounded());
		s.getColor();
		s.getX();
		s2.getColor();
		s2.getY();
	}
}

package com.zghw.base.generic;

/**
 * 只有當你希望使用的型別引數比某個具體型別(以及它的所有子型別)更加“泛化”時--也就是說,當你希望
 * 程式碼能夠跨多個類工作時,時用泛型才有所幫助,通常比簡單的類替換要更復雜
 * 必須檢視所有的程式碼,並確定它是否“足夠複雜”到必須時用泛型的程度
 * @author zghw
 *
 */
class HasF{
	void f(){
		System.out.println();
	}
}
class Manipulator<T>{
	private T obj;
	public Manipulator(T obj){
		this.obj =obj;
	}
	public void set(T obj){
		this.obj = obj;
	}
	public T get(){
		return obj;
	}
}
//沒有泛型的類 比較上面的泛型類 可以發現泛型並沒有任何好處
//T擦除了HasF,就好像在類的宣告中用HasF一樣
class Manipulator1{
	private HasF obj;
	public Manipulator1(HasF obj){
		this.obj =obj;
	}
	public void set(HasF obj){
		this.obj = obj;
	}
	public HasF get(){
		return obj;
	}
}
public class Manipulation {

	public static void main(String[] args) {
		Manipulator<HasF> m = new Manipulator<HasF>(new HasF());
		Manipulator1 m1 = new Manipulator1(new HasF());
		//T擦除了HasF,就好像在類的宣告中用HasF一樣
	}

}

package com.zghw.base.generic;

import java.util.*;

/**
 * 萬用字元
 * 為什麼需要萬用字元?
 * 泛型沒有內建的協變型別
 * 萬用字元
 * 1.? extends T 用作方法返回值 意味著 它可以是任何事物包括子類,而編譯器無法驗證“任何事物”的安全性
 * 2.? super T   用作方法引數 將一個T型別的物件或者從T匯出的任何物件作為引數傳入使用型別的方法
 * 3.?
 * 因此你可能會根據如何能夠向一個泛型型別”寫入“(傳遞給一個方法),
 * 以及如何能夠從一個泛型型別中“讀取”(從一個方法中返回),來左手思考子型別和超型別邊界。
 * @author zghw
 *
 */
class Fruit{}
class Apple extends Fruit{}
class Jonathan extends Apple{}
class Orange extends Fruit{}
public class Covariance{

	public static void main(String[] args) {
		List<Fruit> list1 =new ArrayList<Fruit>();
		List<Apple> list2 =new ArrayList<Apple>();
		//編譯不通過 Apple的List在型別上不等價於Fruit的List,即使Apple是Fruit的子類
		//我們在討論的是容器的型別,而不是容器持有的型別
		//但是,我們想要在兩個型別之間建立某種型別的向上轉型關係,
		//就需要萬用字元
		//List<Fruit> list3 =new ArrayList<Apple>();//編譯不通過 
		
		//List<? extends Fruit> :你可以讀作”具有任何從Fruit繼承的型別的列表“
		//但List不會持有任何型別的Fruit. list3引用沒有指定具體的型別 
		//只是為了向上轉型
		List<? extends Fruit> list3 =new ArrayList<Apple>();
		
		//只接受 add(? extends Fruit e) 不瞭解到底需要Fruit的那個具有子型別
		//因此它不會接受任何Fruit,甚至只接受null
		//? extends Fruit 意味著 它可以是任何事物,而編譯器無法驗證“任何事物”的安全性
		//list3.add(new Apple());//編譯不通過
		//list3.add(new Fruit());//編譯不通過
		//list3.add(new Object());//編譯不通過
		list3.add(null);
		
		writeTo(new ArrayList<Fruit>(),new Apple());
		
	}
	/**
	 * ? super T 只作用泛型引數中,List將持有從T匯出的某種具體型別,這樣就可以安全的將一個T型別的物件
	 *或者從T匯出的任何物件作為引數給List方法
	 * @param fruit
	 * @param item
	 */
	static <T> void writeTo(List<? super T> fruit,T item){
		fruit.add(item);
	}
}

package com.zghw.base.generic;

import java.util.*;

/**
 * 萬用字元 ? extends T ? super T 的應用
 * 
 * @author zghw
 *
 */
public class GenericWriterReader {
	static <T> void writeExact(List<T> list, T item) {
		list.add(item);
	}

	static <T> void writeWild(List<? super T> list, T item) {
		list.add(item);
	}

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

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

	static class ReadWild<T> {
		static <T> T readExact(List<? extends T> list) {
			return list.get(0);
		}
	}

	static List<Fruit> fruitsw = new ArrayList<Fruit>();
	static List<Apple> applesw = new ArrayList<Apple>();
	static List<Fruit> fruits = Arrays.asList(new Fruit());
	static List<Apple> apples = Arrays.asList(new Apple());

	static void w1() {
		writeExact(applesw, new Apple());
		writeExact(fruitsw, new Apple());
	}

	static void w2() {
		writeWild(applesw, new Apple());
		writeWild(fruitsw, new Apple());
	}

	static void r1() {
		Apple a = readExact(apples);
		Fruit f = readExact(fruits);
		f = readExact(apples);
	}

	static void r2() {
		Reader<Fruit> r = new Reader<Fruit>();
		Apple a = r.readExact(apples);
		Fruit f = r.readExact(fruits);
		f = r.readExact(apples);
	}

	static void r3() {
		ReadWild<Fruit> r = new ReadWild<Fruit>();
		Apple a = r.readExact(apples);
		Fruit f = r.readExact(fruits);
		f = r.readExact(apples);
	}

	public static void main(String[] args) {
		w1();
		w2();
		r1();
		r2();
		r3();
	}

}