java之 ------ 泛型【從基礎到加強】

來源:互聯網
上載者:User

標籤:

泛型

基礎篇一、為什麼要泛型1、原因

先看一個集合的例子(至於集合,前面有講解,集合詳解連結,不懂得可以先去看看那篇)

import java.util.ArrayList;import java.util.Collection;import java.util.HashSet;import java.util.Iterator;import cn.hncu.bean.Person;public class CollectionDemo {public static void main(String[] args) {Collection col = new ArrayList();//增 addcol.add(new Person(23,"qwe")); //從jdk1.5以後,可以自動封裝col.add(new Person(22,"df"));col.add(new Person(21,"af"));col.add(new Person(24,"fgr"));col.add(new Person(26,"et"));col.add(new Person(27,"ghj"));//添加重複的元素,對於List是可以的,而對於Set是加不進的。 //刪 remove 移除的是元素本身//col.remove(3);//col.remove(2);//改//col.remove(1); col.add(4); //不合理,因為位置變了//Object objs[] = col.toArray();//col.clear();//for(int i=0;i<objs.length;i++){//if(objs[i].equals(1)){//objs[i]=4;//}//col.add(objs[i]);//}Iterator<Object> it = col.iterator();while(it.hasNext()){Object obj = it.next();if(obj instanceof Person){System.out.println("Object:"+obj);}else if(obj instanceof Integer){System.out.println("int:"+obj);}else{System.out.println("Other:"+obj);}}}}

這個程式就是一個簡單的集合,先輸入和輸出。咋一看沒什麼問題,就算放在myeclipse運行,結果也是對的。但是你會發現,在myeclipse中會出現很多黃色驚嘆號,這表示此程式有警告,也就是存在不安全因素。

會出現這些警告的原因是:集合是可以存放多種不同的種類的元素,但是在取出來的時候就不能確定放的時候到底是什麼類型了。

書面解釋:集合中可以儲存任意類型對象,但是在取出時,如果要使用具體對象的特有方法時,需要進行向下轉型,如果儲存的物件類型不一致,在轉型過程中就會出現ClassCastException異常。這樣就給程式帶來了不安全性。

在jdk1.5以後就有瞭解決方案——泛型技術:在儲存元素時,就不允許儲存不同類型的元素。儲存了就編譯失敗。 所以就需要在儲存元素時,在容器上明確具體的元素類型,這其實和數組定義很像。(數組定義:int[] ,double[] ,類名[],String[]等

等,看看寫法應該能明白一二了吧,前面都明確了數組的類型)

要除去上面的黃色驚嘆號,只要改這兩句即可:

Collection<Person> col = new ArrayList<Person>();Iterator<Person> it = col.iterator();

2、好處

1)將運行時期的ClassCastException異常轉移到了編譯時間期,進行檢查,並以編譯失敗來體現。 這樣有利於程式員儘早解決問題。 

2)避免了向下轉型(強轉)的麻煩。

例:

import java.util.ArrayList;import java.util.Iterator;public class GenericDemo1 {public static void main(String[] args) {//demo1();demo2();}private static void demo1() {ArrayList aList = new ArrayList();aList.add("abcd");aList.add("abcewew");aList.add("a333d");aList.add(8);//編譯沒錯,但運行時出錯了。這是不安全的,對軟體開發來講,很不利。Iterator it = aList.iterator();while(it.hasNext()){String str = (String) it.next();str = str.toUpperCase();System.out.println(str.length());}}private static void demo2() {ArrayList<String> aList = new ArrayList<String>();aList.add("abcd");aList.add("abcewew");aList.add("a333d");//aList.add(8);//加泛型後,該句會直接報錯。泛型的好處(1)Iterator<String> it = aList.iterator();while(it.hasNext()){String str = it.next();//加泛型後,不需要進行類型強轉。泛型的好處(2)str = str.toUpperCase();System.out.println(str.length());}}}
調用demo1()時,放的時候用了兩個不同的類型元素,編譯的時候沒有報錯,輸出的時候,因為沒給泛型,所以要強轉,問題就出現在這,int型的8強轉到string就會出錯,也就是運行時出錯。

調用demo1()時,因為提前用了泛型,也就是規定了這個集合放什麼樣類型的資料,所以在寫完add(8)的時候,就會直接報錯。而且後面輸出的時候,也不用再強轉了。

總而言之,就是,java寫了一個可以同時操作多種不同類型元素功能的容器,也就是集合,但是在運用的它的時候發現存在不安全因素,所以在運用它的時候又規定了只能用哪一種,也就是加的泛型。(如同數組)

二、泛型運用的原理1、什麼時候寫泛型

只要在使用類或者介面時,該類或者介面在api文當描述時都帶著<>,就需要在使用時定義泛型。 其實,泛型無非就是通過<>定義了一個形式參數,專門用於接收具體的參考型別。在使用時,一定要傳遞對應的實際參數類型。集合中泛型的應

用特別多見。

2、泛型的擦除

泛型技術是用於編譯時間期的技術,編譯器會按照<>中的指定類型對元素進行檢查,檢查不匹配,就編譯失敗,匹配,就編譯通過,通過後,生產的class檔案中是沒有泛型的,這就成為泛型的擦除。

3、泛型的補償

運行時,可以根據具體的元素對象擷取其具體的類型,並用該類型對元素進行自動轉換。(也就是取出的時候,不用再用強轉了)

三、泛型的使用1、使用泛型的動機舉例(以集合為例):

對集合中存放元素的類型進行限定,防止後期出錯。如果限定某個集合只能存放Person類對象(因為後期會把元素取出來當作Person對象來處理),這樣放其它類型的對象在編譯時間就會報錯。相當於把一

個類泛化成很多個不同類型(具體化,限定化)的類。

泛型使用的代碼如:

List<Person> persons = new ArrayList<Person>;Map<String,String> m = new HashMap<String,String>;

2、泛型的含義

在定義泛型類或聲明泛型類的變數時,使用角括弧來指定形式型別參數,稱為類型形參,在調用時傳遞的實際類型成為類型實參。類型形參與類型實參之間的關係類似於形式方法參數與實際方法參數之間的關係,只是型別參數表示類型,而不

是表示值。

注意:當一個變數被聲明為泛型時,只能被執行個體變數和方法調用,而不能被靜態變數和方法調用。原因很簡單,參數化的泛型是一些執行個體。靜態成員是被類的執行個體和參數化類別所共用的,所以靜態成員不應該有型別參數和他們關聯。

四、泛型類(自訂)1、定義成泛型類情況

當一個類要操作的引用資料類型不確定的時候,可以將該類型定義一個形參。用到的這類時,由使用者來通過傳遞型別參數的形式,來確定要操作的具體的物件類型。

這意味著在定義這個類時,需要在類上定義形參,用於接收具體的類型實參。這就是將泛型定義在類上,即泛型類。

例:

/* * 泛型類的示範 */public class GenericDemo2 {public static void main(String[] args) {MyVessel<Worker> u = new MyVessel<Worker>();//u.setObject(new Student());//不行,u中存放的是Worker,實參只能是Worker類型u.setObject(new Worker());Worker w = u.getObject();System.out.println(w);MyVessel<Student> v = new MyVessel<Student>();//v.setObject(new Worker());//不行,實參只能是Student類型}}class MyVessel<E>{ //從文法上講把“E”取成別的名字如“QQ”也是可以的,但不規範。private E obj;public void setObject(E obj){this.obj = obj;}public E getObject(){return obj;}}class Student{String profession;}class Worker{String company; }

2、泛型類的好處

什麼時候使用泛型類呢?只要類中操作的引用資料類型不確定的時候,就可以定義泛型類。 有了泛型類,省去了曾經的強轉和類型轉換異常的麻煩。 

3、泛型方法的定義

1)與類的泛型捆綁

方法要操作的類型不確定的,但是和調用該方法的對象指定的類型是一致。

2)獨立於類的泛型

方法要操作的類型不確定的,而且不一定和調用該方法的對象指定的類型是一致。

3)靜態方法的泛型

靜態方法不能訪問類上定義的泛型,因為它沒有對象。如果靜態方法需要泛型,該泛型只能定義在方法上。

例:

/* * 泛型方法示範 */public class GenericDemo3 {public static void main(String[] args) {Tool<String> t1 = new Tool<String>();t1.show("abc");//t1.show(new Integer(6));//不行,因為t1限定只能處理String型資料Tool<Integer> t2 = new Tool<Integer>();//t2.show("abc");//不行,因為t2限定只能處理Integer型資料t2.show(new Integer(6));String s = t1.myprint("aaa");System.out.println(s);int i = t1.myprint(new Integer(6));//String s2 = t1.myprint(new Integer(6));//不行,因為方法上定義了泛型,且傳回型別和方法的實參類型是一致的System.out.println(i);}}class Tool<W>{//方法上的泛型和類的一致,或者說依賴於類的泛型   法1public void show(W w){System.out.println("show:"+w.toString());}//不帶泛型,不安全。因為在調用方可以把該方法的傳回值強轉成其它類型,從而出現強轉異常public Object myprint0(Object a){System.out.println("myprint:"+a);return a;}//方法帶泛型,但要求和類的泛型相互獨立。可以限定傳回型別和方法的實參相同,更安全,而且不用強轉。   法2public <A> A myprint(A a){System.out.println("myprint:"+a);return a;}//靜態方法帶泛型,泛型一定要獨立於類,因為它沒有對象。   法3public static <A> A myprint2(A a){System.out.println("myprint:"+a);return a;}}

五、泛型介面

public class GenericDemo4 {public static void main(String[] args) {String str = new InterImpl().show("abc");System.out.println("main:"+str);//Integer i = new InterImpl2<Integer>().show(80);InterImpl2<Integer> mm = new InterImpl2<Integer>();//mm.show("aaa");//上一句加了泛型,因此這裡會報告編譯錯誤int i = mm.show(80);System.out.println(i);}}interface Inter<V>{public abstract V show(V v);}//實現泛型介面的類的定義。 方法中的參數類型和類聲明實現介面時指定的類型一致,且已經確定為String!-----本例假設我們寫這個類的時候知道該類就是專門處理String型資料的class InterImpl implements Inter<String>{@Overridepublic String show(String s) {System.out.println(s);return s;}}//實現泛型介面的類的定義。 方法中的參數類型和類聲明實現介面時指定的類型一致,但不確定!-----本例假設我們寫這個類的時候不知道該類是處理什麼類型的資料的,但有一點確定:聲明類對象時指定什麼類型(泛型的實參),show方法就處理該類型class InterImpl2<C> implements Inter<C>{@Overridepublic C show(C s) {System.out.println(s);return s;}}



加強篇一、泛型的萬用字元:?

當操作的不同容器中的類型都不確定的時候,而且使用的都是元素從Object類中繼承的方法, 這時泛型就用萬用字元?來表示即可。(助理解的比方: 泛型中的多態應用)

import java.util.ArrayList;import java.util.Iterator;public class GenericAdvDemo1_1 {public static void main(String[] args) {ArrayList<String> a1 = new ArrayList<String>();a1.add("abc1");a1.add("abc2");a1.add("abc3");ArrayList<Integer> a2 = new ArrayList<Integer>();a2.add(100);a2.add(6);a2.add(8);//printColl(a1);//此方法只能輸出a1//printColl2(a2);//<span style="font-family: Arial, Helvetica, sans-serif;">此方法只能輸出a2</span>printColl3(a1);//此方法既能輸出a1也能輸出a2,這就是泛型的萬用字元的用途printColl3(a2);}/* 該解決方案從思路上講是對的,但在文法上通不過。因為這是運行期的多態,而我們的泛型在編譯期public static void printColl(ArrayList<Object> a){Iterator<Object> it = a.iterator();while(it.hasNext()){Object str = it.next();System.out.println(str);}}*/public static void printColl(ArrayList<String> a){Iterator<String> it = a.iterator();while(it.hasNext()){String str = it.next();System.out.println(str);}}public static void printColl2(ArrayList<Integer> a){Iterator<Integer> it = a.iterator();while(it.hasNext()){Integer str = it.next();System.out.println(str);}}public static void printColl3(ArrayList<?> a){Iterator<?> it = a.iterator();while(it.hasNext()){Object str = it.next();System.out.println(str); //元素只能調用從Object類中繼承的方法}}}


二、泛型的限定

對操作的類型限制在一個範圍之內。比如:定義一個功能,只操作Person類型或者Person的子類型。這時可以用:  

? extends E:接收E類型或者E的子類型。這就是上限。 

import java.util.ArrayList;import java.util.Iterator;public class GenericAdvDemo3 {public static void main(String[] args) {ArrayList<Person> a1 = new ArrayList<Person>();a1.add(new Person("Jack1",20));a1.add(new Person("Jack3",23));a1.add(new Person("Jack2",22));ArrayList<Integer> a2 = new ArrayList<Integer>();a2.add(100);a2.add(6);a2.add(8);ArrayList<Student> a3 = new ArrayList<Student>();a3.add(new Student("aa",10));a3.add(new Student("bb",20));a3.add(new Student("cc",30));printColl(a1);//printColl(a2);printColl(a3);}/*自己寫帶上限的泛型方法*/public static void printColl(ArrayList<? extends Person> a){Iterator<? extends Person> it = a.iterator();while(it.hasNext()){Person p = it.next();System.out.println(p.getName()); //元素只能調用從Person類中繼承的方法}}}


? super E:   接收E類型或者E的父類型。 這就是下限。

API查看:Collection中的addAll()方法中的泛型就是一個上限的運用。TreeSet類的構造方法就分別用了上限和下限。

一般情況下:

只要是往容器中添加元素時,使用上限。 ? extends E

只要是從容器中取出元素時,是用下限。 ? super E

import java.util.ArrayList;import java.util.Collection;import java.util.Comparator;import java.util.Iterator;import java.util.TreeSet;public class GenericAdvDemo2 {public static void main(String[] args) {//示範上限,應用場合:往集合中放元素//extendsEDemo();//示範下限,應用場合:從集合中取元素superEDemo();}private static void extendsEDemo() {Collection<Person> coll = new ArrayList<Person>();coll.add(new Person("Jack",23));coll.add(new Person("Tom",124));coll.add(new Person("Rose",25));Collection<Student> coll2 = new ArrayList<Student>();coll2.add(new Student("Jack2",223));coll2.add(new Student("Tom2",224));coll2.add(new Student("Rose2",225));TreeSet<Person> ts = new TreeSet<Person>(coll);ts.add(new Student("aaa",100));ts.addAll(coll2);//上限:coll2中的元素類型(泛型)必須是Person或Person的子類Iterator<Person> it = ts.iterator();while(it.hasNext()){Person p = it.next();System.out.println(p.toString());}//TreeSet<Student> ts2 = new TreeSet<Student>(coll2);//Iterator<Student> it2 = ts2.iterator();//while(it2.hasNext()){//Student p = it2.next();//System.out.println(p.toString());//}}private static void superEDemo() {//TreeSet<Student> ts = new TreeSet<Student>( new CompByName1() );TreeSet<Student> ts = new TreeSet<Student>( new CompByName() );ts.add(new Student("lisi1",21));ts.add(new Student("lisi3",23));ts.add(new Student("lisi2",22));Iterator<Student> it = ts.iterator();while(it.hasNext()){Student p = it.next();System.out.println(p.toString());}//TreeSet<Worker> ts2 = new TreeSet<Worker>( new CompByName2() );TreeSet<Worker> ts2 = new TreeSet<Worker>( new CompByName() );ts2.add(new Worker("lisi21",21));ts2.add(new Worker("lisi23",23));ts2.add(new Worker("lisi22",22));Iterator<Worker> it2 = ts2.iterator();while(it2.hasNext()){Worker p = it2.next();System.out.println(p.toString());}}}@SuppressWarnings("rawtypes")class Person implements Comparable{String name;int age;public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic int compareTo(Object o) {Person p = (Person) o;return this.age-p.age;}@Overridepublic String toString() {return "Person [name=" + name + ", age=" + age + "]";}}class Student extends Person{String profession;public Student(String name, int age) {super(name, age);}}class CompByName1 implements Comparator<Student>{@Overridepublic int compare(Student s1, Student s2) {return s1.getName().compareTo(s2.getName());}}class Worker extends Person{String company;public Worker(String name, int age) {super(name, age);}}class CompByName2 implements Comparator<Worker>{@Overridepublic int compare(Worker s1, Worker s2) {return s1.getName().compareTo(s2.getName());}}//利用泛型中的“Comparator<? super E> comparator”即下限特性,做一個通用的比較子(比較所有Person及其子類對象0class CompByName implements Comparator<Person>{@Overridepublic int compare(Person s1, Person s2) {return s1.getName().compareTo(s2.getName());}}















著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

java之 ------ 泛型【從基礎到加強】

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.