乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      java 泛型編程(一)

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

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

      一、java 泛型引入

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

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


           上面的代碼會在運行時拋出ClassCastException,因為它嘗試將一個Integer轉(zhuǎn)換為String。接著,來看一下從java5開始,Collection的用法:

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


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

      二、泛型的類與接口

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

      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)換,可能會引起運行時ClassCastException
      	}
      }


          原始類的定義,容易引發(fā)ClassCastException,和第一大點談到的類似。現(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);	//可以通過編譯,自動裝箱將10轉(zhuǎn)化為Integer對象
      		Integer num = (Integer) gen2.getObj();//使用了強制類型轉(zhuǎn)換
      	}
      }

          細心的你會發(fā)現(xiàn)在main()方法里是使用泛型類型Gen<String>,便不再需要強制類型轉(zhuǎn)換,也就移除了運行時的ClassCastException。同時為了區(qū)別,在此也定義了一個沒有使用泛型類型的gen2,這時,編譯器會彈出一個警告“Gen is a raw type,References to generic type Gen<T> should be parameterized”。當我們不提供泛型類型時,會默認使用Object會代替,也是因此這樣,gen2可以設(shè)置String和Integer類型,不過,我們應(yīng)盡量去避免這種這種情況的出現(xiàn),如此,便又需要用到強制類型轉(zhuǎn)換,也伴隨著運行時的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)用到自定義的接口與類當中。另外再提一下的是,可以使用多個泛型參數(shù)來定義接口與類,比如Map<K,V>;同時,泛型類型也可以作為一個參數(shù)來用,如下:new HashMap<String, List<String>>()。

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

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

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

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

          有時候我們并不希望整個類都被泛型化,這時可以只在某個方法上應(yīng)用泛型。因為構(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>,如果有多個泛型參數(shù),可如此定義<K,V>或者<T1,T2>。第二,不建議在泛型變量里添加其他類型,如下面的代碼,將會引起編譯錯誤(或隱含錯誤),如下:

      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。不過因為編譯器可以推斷這個泛型類型,因此也可以這樣寫:

          GenMethod.fromArrayToCollection(oa, co)。

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

      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);
      
      //編譯錯誤,Number與String不能兼容
      fromArrayToCollection(na, cs);

       

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

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

          <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");	//能通過編譯,因為T指定為String類型
      //		integerBox.inspect("abc");//不能通過編譯,因為U必須是Number類型或其子類
      		integerBox.inspect(new Integer(10));
      	}
      }


          通過Box<T>,了解了如何為泛型參數(shù)添加一個界限。可問題也來了,既然限定了泛型參數(shù)的界限,那時候可以調(diào)用BoundingType(上指Number)的相對應(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;
      	}
      	
      	//....
      }

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

          <T extends A & B & C>

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

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


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

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

         

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

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

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


          發(fā)現(xiàn)上面這個方法無法通過編譯,為什么呢??因為操作符“>”只可以用在基本數(shù)據(jù)類型(byte,char,short,int,float,long,double,boolean),卻不可以用來比較類對象之間的大?。ǔ菍崿F(xiàn)了Comparable接口)。想要解決這個矛盾,就需要為<T>添加一個界限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) {//無編譯錯誤
      				++count;
      			}
      		}
      		return  count;
      	}

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

      六、泛型、繼承與子類型

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

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

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

      public void someMethod(Number n) { /* ... */ }
      
      someMethod(new Integer(10));   // OK
      someMethod(new Double(10.1);   // OK

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

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


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

      public void someMethod(Box<Number> n) { /*.....*/}
          這個方法可以接受什么類型的參數(shù)呢??顯然,這個方法接受Box<Number>類型的參數(shù)??那又是否可以接受Box<Integer>或者Box<Double>類型的參數(shù)的??答案是否定的,因為Box<Integer>與Box<Double>都不是Box<Number>的子類。在泛型編程里,這是一個容易混淆的概念,但又必須要懂的原理。如下圖:
      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。那是否說,泛型就不存在子類呢??這個留待解決,看完本文便可以知曉。

      七、泛型類與子類型

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

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

      generics-sampleHierarchy

          深入一點來談,現(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

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

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多