久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

java 泛型編程(一)

 一本正經(jīng)地胡鬧 2019-07-13
Technorati 標(biāo)記: ,,

    java泛型應(yīng)用是java核心基礎(chǔ)之一,,從java 5開始引進(jìn)泛型。如果你曾經(jīng)使用過java Collection,,那你已經(jīng)算是接觸過泛型了,。在java Collection里使用泛型是一件很簡單的事情,可泛型還具有很多你意想不到的作用,。在深入了解泛型之前,,首先來了解一下泛型的一些基本概念與原理。

一,、java 泛型引入

    java泛型的應(yīng)用可以提高的代碼的復(fù)用性,,同時(shí)泛型提供了類型檢查,減少了數(shù)據(jù)的類型轉(zhuǎn)換,,同時(shí)保證了類型安全,。下面看一下,泛型如何保證了類型安全:

List list = new ArrayList();
list.add("abc");
list.add(new Integer(1));	//可以通過編譯
for (Object object : list) {
	System.out.println((String)object);//拋出ClassCastException異常
}


     上面的代碼會(huì)在運(yùn)行時(shí)拋出ClassCastException,,因?yàn)樗鼑L試將一個(gè)Integer轉(zhuǎn)換為String,。接著,來看一下從java5開始,,Collection的用法:

List<String> list = new ArrayList<>();
list.add("abc");
//list.add(new Integer(1));	//編譯錯(cuò)誤
for (String string : list) {
	System.out.println(string);//無需任何強(qiáng)制類型轉(zhuǎn)換
}


    注意到,,List的創(chuàng)建增加了類型參數(shù)String,因此只能向list添加String類型對(duì)象,,添加其他對(duì)象會(huì)拋出編譯異常,;同樣可以注意到,foreach循環(huán)不需要再添加任何強(qiáng)制類型轉(zhuǎn)換,,也就移除了運(yùn)行時(shí)的ClassCastException異常,。

二、泛型的類與接口

    既然是學(xué)泛型,,自然就要知道如何去使用泛型定義自己的類和接口,。同時(shí)為了加深理解泛型的作用,先引進(jìn)一個(gè)原始的類:

public class Gen {
	private Object obj;
	
	public Object getObj() {
		return obj;
	}

	public void setObj(Object obj) {
		this.obj = obj;
	}
	
	public static void main(String[] args) {
		Gen gen = new Gen();
		gen.setObj("abc");
		String str = (String) gen.getObj();//類型轉(zhuǎn)換,,可能會(huì)引起運(yùn)行時(shí)ClassCastException
	}
}


    原始類的定義,,容易引發(fā)ClassCastException,和第一大點(diǎn)談到的類似?,F(xiàn)在來看一下泛型類來重新定義Gen — 使用<>指定泛型參數(shù),,如下:

public class Gen<T> {
	T obj;

	public T getObj() {
		return obj;
	}

	public void setObj(T obj) {
		this.obj = obj;
	}
	public static void main(String[] args) {
		Gen<String> gen = new Gen<>();
		gen.setObj("abc");
//		gen.setObj(10);		//無法通過編譯
		String str = gen.getObj();	//無需類型轉(zhuǎn)換
		//-----------------------------
		Gen gen2 = new Gen();//raw type原始類型
		gen2.setObj("abc");
		gen2.setObj(10);	//可以通過編譯,自動(dòng)裝箱將10轉(zhuǎn)化為Integer對(duì)象
		Integer num = (Integer) gen2.getObj();//使用了強(qiáng)制類型轉(zhuǎn)換
	}
}

    細(xì)心的你會(huì)發(fā)現(xiàn)在main()方法里是使用泛型類型Gen<String>,便不再需要強(qiáng)制類型轉(zhuǎn)換,,也就移除了運(yùn)行時(shí)的ClassCastException,。同時(shí)為了區(qū)別,在此也定義了一個(gè)沒有使用泛型類型的gen2,,這時(shí),,編譯器會(huì)彈出一個(gè)警告“Gen is a raw type,References to generic type Gen<T> should be parameterized”。當(dāng)我們不提供泛型類型時(shí),,會(huì)默認(rèn)使用Object會(huì)代替,,也是因此這樣,gen2可以設(shè)置String和Integer類型,,不過,,我們應(yīng)盡量去避免這種這種情況的出現(xiàn),如此,,便又需要用到強(qiáng)制類型轉(zhuǎn)換,,也伴隨著運(yùn)行時(shí)的ClassCastException異常。

    tips:可以使用@SuppressWarnings("rawtypes")來抑制編譯器彈出警告,。

    接口的泛型應(yīng)用和類的泛型應(yīng)用很類似,,如下:

public interface List <E> {
	 void add(E x);
	 Iterator<E> iterator();
}

public interface Iterator<E> {
	 E next();
	 boolean hasNext();
}

    類似的,可以將此應(yīng)用到自定義的接口與類當(dāng)中,。另外再提一下的是,,可以使用多個(gè)泛型參數(shù)來定義接口與類,比如Map<K,V>,;同時(shí),,泛型類型也可以作為一個(gè)參數(shù)來用,如下:new HashMap<String, List<String>>(),。

三,、泛型的命名規(guī)范

    為了更好地去理解泛型,我們也需要去理解java泛型的命名規(guī)范,。為了與java關(guān)鍵字區(qū)別開來,,java泛型參數(shù)只是使用一個(gè)大寫字母來定義。各種常用泛型參數(shù)的意義如下:

  • E — Element,常用在java Collection里,,如:List<E>,Iterator<E>,Set<E>
  • K,V — Key,,Value,代表Map的鍵值對(duì)
  • N — Number,,數(shù)字
  • T — Type,,類型,如String,,Integer等等
  • S,U,V etc. - 2nd, 3rd, 4th 類型,,和T的用法一樣

四、泛型的方法與構(gòu)造函數(shù)

    有時(shí)候我們并不希望整個(gè)類都被泛型化,,這時(shí)可以只在某個(gè)方法上應(yīng)用泛型,。因?yàn)闃?gòu)造函數(shù)是一種特殊的方法,因此也可以在構(gòu)造函數(shù)上應(yīng)用泛型,。Demo GenMethod演示了如何在方法上應(yīng)用泛型和調(diào)用泛型方法,,

public class GenMethod {
	
	public static <T> void fromArrayToCollection(T[] a,Collection<T> c){
		for (T t : a) {
		  c.add(t);
		}
	}
	
	public static void main(String[] args) {
		Object[] oa = new Object[100];
		Collection<Object> co = new ArrayList<>();
		
		GenMethod.<Object>fromArrayToCollection(oa, co);
	}		
}


    GenMethod 的代碼不多,不過需要注意的地方卻不少,。第一,、定義方法所用的泛型參數(shù)需要在修飾符之后添加,如上面的,,public static <T>,如果有多個(gè)泛型參數(shù),,可如此定義<K,V>或者<T1,T2>。第二,,不建議在泛型變量里添加其他類型,,如下面的代碼,將會(huì)引起編譯錯(cuò)誤(或隱含錯(cuò)誤),,如下:

public static <T> void fromArrayToCollection(T[] a,Collection<T> c){
		for (T t : a) {
			c.add(t);
			c.add(new Object());	
		}
	}


    第三,、看一下泛型方法的調(diào)用GenMethod.<Object>fromArrayToCollection(oa, co); 在方法前聲明了泛型類型Object。不過因?yàn)榫幾g器可以推斷這個(gè)泛型類型,,因此也可以這樣寫:

    GenMethod.fromArrayToCollection(oa, co),。

    為了加深對(duì)編譯器推斷泛型類型的了解,再看一下如下幾個(gè)推斷:

String[] sa = new String[100];
Collection<String> cs = new ArrayList<String>();

// T 推斷為String
fromArrayToCollection(sa, cs);

// T 推斷為Object
fromArrayToCollection(sa, co);

Integer[] ia = new Integer[100];
Float[] fa = new Float[100];
Number[] na = new Number[100];
Collection<Number> cn = new ArrayList<Number>();

//T 推斷為Number
fromArrayToCollection(ia, cn);

//T 推斷為Number
fromArrayToCollection(fa, cn);

//T 推斷為Number
fromArrayToCollection(na, cn);

//T 推斷為Object
fromArrayToCollection(na, co);

//編譯錯(cuò)誤,,Number與String不能兼容
fromArrayToCollection(na, cs);

 

四,、泛型參數(shù)的界限

    有時(shí)候,你會(huì)希望泛型類型只能是某一部分類型,,比如操作數(shù)據(jù)的時(shí)候,,你會(huì)希望是Number或其子類類型。這個(gè)想法其實(shí)就是給泛型參數(shù)添加一個(gè)界限,。其定義形式為:

    <T extends BoundingType>

    此定義表示T應(yīng)該是BoundingType的子類型(subtype),。T和BoundingType可以是類,,也可以是接口。另外注意的是,,此處的”extends“表示的子類型,,不等同于繼承。

    Demo:

public class Box<T> {

	private T t;

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

	public T get() {
		return t;
	}

	public <U extends Number> void inspect(U u) {
		System.out.println("T: " + t.getClass().getName());
		System.out.println("U: " + u.getClass().getName());
	}

	public static void main(String[] args) {
		Box<String> integerBox = new Box<>();
		integerBox.set("abc");	//能通過編譯,,因?yàn)門指定為String類型
//		integerBox.inspect("abc");//不能通過編譯,,因?yàn)閁必須是Number類型或其子類
		integerBox.inspect(new Integer(10));
	}
}


    通過Box<T>,,了解了如何為泛型參數(shù)添加一個(gè)界限,。可問題也來了,,既然限定了泛型參數(shù)的界限,,那時(shí)候可以調(diào)用BoundingType(上指Number)的相對(duì)應(yīng)的方法呢?,?答案是肯定的,,如下:

public class NumberTest<T extends Integer> {
	private T num;
	
	public NumberTest(T num) { this.num = num;}
	
	public boolean isOdd(){
		return num.intValue()%2 == 1;
	}
	
	//....
}

    接著引入下一個(gè)問題,如何為泛型參數(shù)添加多個(gè)限制范圍,,多重限制范圍格式如下:

    <T extends A & B & C>

    一個(gè)泛型參數(shù)可以有多重限制范圍,,使用“&”分隔。且限制范圍中之多有一個(gè)類,。如果用一個(gè)類作為限定,,它必須是限定列表中的第一個(gè)。舉例如下:

Class A { /* ... */ }
interface B { /* ... */ }
interface C { /* ... */ }

class D <T extends A & B & C> { /* ... */ }


    如果BoundingType不是放在第一位,,會(huì)產(chǎn)生編譯異常:

class D <T extends B & A & C> { /* ... */ }  // 無法通過編譯

   

五,、泛型方法與泛參界限的綜合

    如果說泛型方法是一個(gè)有用的工具,那泛參的界限就應(yīng)該這個(gè)工具的靈魂,,為這個(gè)工具添加了一些“行為準(zhǔn)則”,。如下:設(shè)計(jì)一個(gè)方法,統(tǒng)計(jì)在一個(gè)數(shù)組里比指定元素大的個(gè)數(shù),,

public static <T> int countGreater(T[] array,T elem) {
		int count = 0;
		for (T t : array) {
			if (t > elem) {//編譯錯(cuò)誤
				++count;
			}
		}
		return  count;
	}


    發(fā)現(xiàn)上面這個(gè)方法無法通過編譯,,為什么呢?,?因?yàn)椴僮鞣?gt;”只可以用在基本數(shù)據(jù)類型(byte,,char,short,int,,float,,long,double,,boolean),,卻不可以用來比較類對(duì)象之間的大小(除非實(shí)現(xiàn)了Comparable接口)。想要解決這個(gè)矛盾,,就需要為<T>添加一個(gè)界限Comparable<T>:

public interface Comparable<T> {
    public int compareTo(T o);
}

    更改后的代碼如下:

public static <T extends Comparable<T>> int countGreater(T[] array,T elem) {
		int count = 0;
		for (T t : array) {
			if (t.compareTo(elem) > 0) {//無編譯錯(cuò)誤
				++count;
			}
		}
		return  count;
	}

     除了上述方式,,也可以選擇添加界限Comparator<T,T>,只不過此界限需要兩個(gè)參數(shù)而已,Comparator的定義與使用以前已經(jīng)談過,,這里不再累述,,詳情可以點(diǎn)擊 這里

六,、泛型,、繼承與子類型

    如果兩個(gè)類之間相互兼容(繼承與被繼承),那么便可以將一個(gè)類對(duì)象賦值給另一個(gè)類對(duì)象,,比如:你可以將一個(gè)String對(duì)象賦值給Object,,String是Object的子類,

String someString = new String();
Object someObject = new Object();
someObject = someString;

    如果你熟悉面向?qū)ο蠹夹g(shù),,會(huì)知道這是一種“is-a”關(guān)系,。String是Object的一種對(duì)象,所以上面的賦值是可以的,。同理,,Integer、Double是Number的一類對(duì)象,,下面的賦值也可以:

public void someMethod(Number n) { /* ... */ }

someMethod(new Integer(10));   // OK
someMethod(new Double(10.1);   // OK

    這種“is-a”關(guān)系,,同樣也是用泛型。如果你將泛參設(shè)置Number,,那么在隨后的調(diào)用里,,只需要傳入一個(gè)數(shù)據(jù)對(duì)象就行了,如下:

Box<Number> box = new Box<>();
box.add(new Integer(1));   
box.add(new Double(1.0));


     現(xiàn)在,,考慮一下下面這種方法:

public void someMethod(Box<Number> n) { /*.....*/}
    這個(gè)方法可以接受什么類型的參數(shù)呢,??顯然,,這個(gè)方法接受Box<Number>類型的參數(shù),??那又是否可以接受Box<Integer>或者Box<Double>類型的參數(shù)的,?,?答案是否定的,因?yàn)锽ox<Integer>與Box<Double>都不是Box<Number>的子類,。在泛型編程里,,這是一個(gè)容易混淆的概念,但又必須要懂的原理,。如下圖:
generics-subtypeRelationship
    從圖可以看到,,即使Integer是Number的子類,,但Box<Integer>并不是Box<Number>的子類。Box<Integer>與Box<Number>的共同父類是Object,。換言之,,無論類A與類B是否存在關(guān)聯(lián),MyClass<A>與MyClass<B>都沒有任何關(guān)聯(lián),,其共同的父類的是Object,。那是否說,泛型就不存在子類呢,?,?這個(gè)留待解決,看完本文便可以知曉,。

七,、泛型類與子類型

    在談這一小節(jié)時(shí),先回顧一下泛型方法的“extends”含義,,泛型的“extends”與繼承的“extends”并不一樣,泛型的“extends”其后可以是一個(gè)類(如T extends Number),,同樣也可以是一個(gè)接口(如T extends List<T>),。泛型的”extends“代表子類型,而不是子類,,或許你可以把等其同于”extends(繼承)“和”implement的并集,。

    在泛型里,也存在子類型,,前提是其泛型參數(shù)的限制并沒有改變,,可以認(rèn)為泛參沒有改變,其實(shí)就是從原來的類或接口來判斷泛型的子類型,。為了形象理解,,我們已collection類來作個(gè)例子,如:ArrayList<E> implement List<E>,,而List<E> extends  Collection<E>,,那么ArrayList<String>就是List<String>的子類型,而List<String>則是Collection<String>,其關(guān)系圖如下:

generics-sampleHierarchy

    深入一點(diǎn)來談,,現(xiàn)在假設(shè)需要定義自己的List接口 — PayLoadList,,其定義如下:

interface PayloadList<E,P> extends List<E> {
  void setPayload(int index, P val);
  //...
}

    如上,則下面的樣例都是List<String>子類型,:

PayloadList<String,String>
PayloadList<String,Integer>
PayloadList<String,Exception>
  如圖:
generics-payloadListHierarchy
轉(zhuǎn)自 https://blog.51cto.com/peiquan/1302898

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請遵守用戶 評(píng)論公約

    類似文章 更多