您好,登錄后才能下訂單哦!
小編給大家分享一下Java中泛型的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
早期的Object類型可以接收任意的對象類型,但是在實際的使用中,會有類型轉換的問題。也就存在這隱患,所以Java提供了泛型來解決這個安全問題。
來看一個經典案例:
public static void main(String[] args) { //測試一下泛型的經典案例 ArrayList arrayList = new ArrayList(); arrayList.add("helloWorld"); arrayList.add("taiziyenezha"); arrayList.add(88);//由于集合沒有做任何限定,任何類型都可以給其中存放 for (int i = 0; i < arrayList.size(); i++) { //需求:打印每個字符串的長度,就要把對象轉成String類型 String str = (String) arrayList.get(i); System.out.println(str.length()); } }
運行這段代碼,程序在運行時發生了異常:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
發生了數據類型轉換異常,這是為什么?
由于ArrayList
可以存放任意類型的元素。例子中添加了一個String類型,添加了一個Integer類型,再使用時都以String的方式使用,導致取出時強制轉換為String類型后,引發了ClassCastException
,因此程序崩潰了。
這顯然不是我們所期望的,如果程序有潛在的錯誤,我們更期望在編譯時被告知錯誤
,而不是在運行時報異常。而為了解決類似這樣的問題(在編譯階段就可以解決),在jdk1.5后,泛型應運而生。讓你在設計API時可以指定類或方法支持泛型,這樣我們使用API的時候也變得更為簡潔,并得到了編譯時期的語法檢查。
我們將第一行聲明初始化ArrayList的代碼更改一下,編譯器就會在編譯階段就能夠幫我們發現類似這樣的問題。現在再看看效果。
ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("helloWorld"); arrayList.add("taiziyenezha"); arrayList.add(88);// 在編譯階段,編譯器就會報錯
這樣可以避免了我們類型強轉時出現異常。
泛型:是一種把明確類型的工作推遲到創建對象或者調用方法的時候才去明確的特殊的類型。也就是說在泛型使用過程中,操作的數據類型被指定為一個參數,而這種參數類型可以用在類、方法和接口中,分別被稱為泛型類
、泛型方法
、泛型接口
。
注意:一般在創建對象時,將未知的類型確定具體的類型。當沒有指定泛型時,默認類型為Object類型。
避免了類型強轉的麻煩。
它提供了編譯期的類型安全,確保在泛型類型(通常為泛型集合)上只能使用正確類型的對象,避免了在運行時出現ClassCastException。
泛型雖然通常會被大量的使用在集合當中,但是我們也可以完整的學習泛型只是。泛型有三種使用方式,分別為:泛型類、泛型方法、泛型接口。將數據類型作為參數進行傳遞。
泛型類型用于類的定義中,被稱為泛型類。通過泛型可以完成對一組類的操作對外開放相同的接口。最典型的就是各種集合框架容器類,如:List、Set、Map。
泛型類的定義格式:
修飾符 class 類名<代表泛型的變量> { }
怕你不清楚怎么使用,這里我還是做了一個簡單的泛型類:
/** * @param <T> 這里解釋下<T>中的T: * 此處的T可以隨便寫為任意標識,常見的有T、E等形式的參數表示泛型 * 泛型在定義的時候不具體,使用的時候才變得具體。 * 在使用的時候確定泛型的具體數據類型。即在創建對象的時候確定泛型。 */ public class GenericsClassDemo<T> { //t這個成員變量的類型為T,T的類型由外部指定 private T t; //泛型構造方法形參t的類型也為T,T的類型由外部指定 public GenericsClassDemo(T t) { this.t = t; } //泛型方法getT的返回值類型為T,T的類型由外部指定 public T getT() { return t; } }
泛型在定義的時候不具體,使用的時候才變得具體。在使用的時候確定泛型的具體數據類型。即:在創建對象的時候確定泛型。
例如:Generic<String> genericString = new Generic<String>("helloGenerics");
此時,泛型標識T的類型就是String類型,那我們之前寫的類就可以這么認為:
public class GenericsClassDemo<String> { private String t; public GenericsClassDemo(String t) { this.t = t; } public String getT() { return t; } }
當你的泛型類型想變為Integer類型時,也是很方便的。直接在創建時,T寫為Integer類型即可:
Generic<Integer> genericInteger = new Generic<Integer>(666);
注意: 定義的泛型類,就一定要傳入泛型類型實參么?
并不是這樣,在使用泛型的時候如果傳入泛型實參,則會根據傳入的泛型實參做相應的限制,此時泛型才會起到本應起到的限制作用。如果不傳入泛型類型實參的話,在泛型類中使用泛型的方法或成員變量定義的類型可以為任何的類型。即跟之前的經典案例一樣,沒有寫ArrayList
的泛型類型,容易出現類型強轉的問題。
泛型方法,是在調用方法的時候指明泛型的具體類型 。
定義格式:
修飾符 <代表泛型的變量> 返回值類型 方法名(參數){ }
例如:
/** * * @param t 傳入泛型的參數 * @param <T> 泛型的類型 * @return T 返回值為T類型 * 說明: * 1)public 與 返回值中間<T>非常重要,可以理解為聲明此方法為泛型方法。 * 2)只有聲明了<T>的方法才是泛型方法,泛型類中的使用了泛型的成員方法并不是泛型方法。 * 3)<T>表明該方法將使用泛型類型T,此時才可以在方法中使用泛型類型T。 * 4)與泛型類的定義一樣,此處T可以隨便寫為任意標識,常見的如T、E等形式的參數常用于表示泛型。 */ public <T> T genercMethod(T t){ System.out.println(t.getClass()); System.out.println(t); return t; }
調用方法時,確定泛型的類型
public static void main(String[] args) { GenericsClassDemo<String> genericString = new GenericsClassDemo("helloGeneric"); //這里的泛型跟下面調用的泛型方法可以不一樣。 String str = genericString.genercMethod("hello");//傳入的是String類型,返回的也是String類型 Integer i = genericString.genercMethod(123);//傳入的是Integer類型,返回的也是Integer類型 }
這里我們可以看下結果:
class java.lang.String
hello
class java.lang.Integer
123
這里可以看出,泛型方法隨著我們的傳入參數類型不同,他得到的類型也不同。泛型方法能使方法獨立于類而產生變化。
泛型接口與泛型類的定義及使用基本相同。泛型接口常被用在各種類的生產器中。
定義格式
修飾符 interface接口名<代表泛型的變量> { }
看一下下面的例子,你就知道怎么定義一個泛型接口了:
/** * 定義一個泛型接口 */ public interface GenericsInteface<T> { public abstract void add(T t); }
使用格式
1、定義類時確定泛型的類型
public class GenericsImp implements GenericsInteface<String> { @Override public void add(String s) { System.out.println("設置了泛型為String類型"); } }
2、始終不確定泛型的類型,直到創建對象時,確定泛型的類型
public class GenericsImp<T> implements GenericsInteface<T> { @Override public void add(T t) { System.out.println("沒有設置類型"); } }
確定泛型:
public class GenericsTest { public static void main(String[] args) { GenericsImp<Integer> gi = new GenericsImp<>(); gi.add(66); } }
當使用泛型類或者接口時,傳遞的數據中,泛型類型不確定,可以通過通配符<?>表示。但是一旦使用泛型的通配符后,只能使用Object類中的共性方法,集合中元素自身方法無法使用。
泛型的通配符:不知道使用什么類型來接收的時候,此時可以使用?,?表示未知通配符。
此時只能接受數據,不能往該集合中存儲數據。
舉個例子大家理解使用即可:
// ?代表可以接收任意類型 // 泛型不存在繼承、多態關系,泛型左右兩邊要一樣 //ArrayList<Object> list = new ArrayList<String>();這種是錯誤的 //泛型通配符?:左邊寫<?> 右邊的泛型可以是任意類型 ArrayList<?> list1 = new ArrayList<Object>(); ArrayList<?> list2 = new ArrayList<String>(); ArrayList<?> list3 = new ArrayList<Integer>();
注意:泛型不存在繼承、多態關系,泛型左右兩邊要一樣,jdk1.7后右邊的泛型可以省略
而泛型通配符?,右邊的泛型可以是任意類型。
泛型通配符?主要應用在參數傳遞方面,讓我們一起瞧瞧唄:
public static void main(String[] args) { ArrayList<Integer> list1 = new ArrayList<Integer>(); test(list1); ArrayList<String> list2 = new ArrayList<String>(); test(list2); } public static void test(ArrayList<?> coll){ }
嘿嘿,是不是見識到了通配符的厲害,可以傳遞不同類似進去方法中了!
之前設置泛型的時候,實際上是可以任意設置的,只要是類就可以設置。但是在JAVA的泛型中可以指定一個泛型的上限和下限。
泛型的上限:
格式: 類型名稱 <? extends 類 > 對象名稱
意義: 只能接收該類型及其子類
泛型的下限:
格式: 類型名稱 <? super 類 > 對象名稱
意義: 只能接收該類型及其父類型
比如:現已知Object類,Animal類,Dog類,Cat類,其中Animal是Dog,Cat的父類
class Animal{}//父類 class Dog extends Animal{}//子類 class Cat extends Animal{}//子類
首先我們先看下,泛型的上限<? extends 類 >:
// ArrayList<? extends Animal> list = new ArrayList<Object>();//報錯 ArrayList<? extends Animal> list2 = new ArrayList<Animal>(); ArrayList<? extends Animal> list3 = new ArrayList<Dog>(); ArrayList<? extends Animal> list4 = new ArrayList<Cat>();
可以看出,泛型的上限只能是該類型的類型及其子類。
我們再來看看泛型的下限<? super 類 >:
ArrayList<? super Animal> list5 = new ArrayList<Object>(); ArrayList<? super Animal> list6 = new ArrayList<Animal>(); // ArrayList<? super Animal> list7 = new ArrayList<Dog>();//報錯 // ArrayList<? super Animal> list8 = new ArrayList<Cat>();//報錯
可以看出,泛型的下限只能是該類型的類型及其父類。
一般泛型的上限和下限也是用來參數的傳遞:
再比如:現已知Object類,String 類,Number類,Integer類,其中Number是Integer的父類
public static void main(String[] args) { Collection<Integer> list1 = new ArrayList<Integer>(); Collection<String> list2 = new ArrayList<String>(); Collection<Number> list3 = new ArrayList<Number>(); Collection<Object> list4 = new ArrayList<Object>(); getElement(list1); getElement(list2);//報錯 getElement(list3); getElement(list4);//報錯 getElement2(list1);//報錯 getElement2(list2);//報錯 getElement2(list3); getElement2(list4); } // 泛型的上限:此時的泛型?,必須是Number類型或者Number類型的子類 public static void getElement1(Collection<? extends Number> coll){} // 泛型的下限:此時的泛型?,必須是Number類型或者Number類型的父類 public static void getElement2(Collection<? super Number> coll){} 118060630
以上是“Java中泛型的示例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。