从0到掌握Java泛型有这一篇博客就够了
1.泛型的概述
2.泛型類
2.泛型接口
4.泛型方法
5.泛型通配符
6.泛型數組
1.泛型的概述
在Java SE 1.5之前,沒有泛型的情況的下,通過對類型Object的引用來實現參數的“任意化”,“任意化”帶來的缺點是要做顯式的強制類型轉換,而這種轉換是要求開發者對實際參數類型可以預知的情況下進行的。對于強制類型轉換錯誤的情況,編譯器可能不提示錯誤,在運行的時候才出現異常,這是一個安全隱患。泛型的好處是在編譯的時候檢查類型安全,并且所有的強制轉換都是自動和隱式的,提高代碼的重用率。
我們舉個例子:
那么為啥會拋出異常呢:
這個例子就是典型的編譯時正常,運行出錯
ArrayList可以存放任意類型,例子中添加了一個String類型,添加了一個Integer類型,再使用時都以String的方式使用,因此程序崩潰了。為了解決類似這樣的問題(在編譯階段就可以解決),泛型應運而生。
那么我們用泛型如何解決這個問題呢,看下面代碼:
package untl2; import java.util.ArrayList; import java.util.List; public class MyType {public static void main(String[] args) {List<String> arrayList = new ArrayList<String>();arrayList.add("aaaa");arrayList.add(100);for(int i = 0; i< arrayList.size();i++) {String item = arrayList.get(i);System.out.println(item);}} }此代碼編譯不通過,由于泛型已經規定arrayList是String類型,但是100是int類型,所以編譯直接報錯,這個時候就體現出泛型的優勢了,而且僅僅就在List后邊加了<String>就能為所欲為了
那么對于這兩段代碼時候給出總結了泛型的兩大優點:
1.將運行時期的ClassCastException,轉移到了編譯時期變成了編譯失敗
2.避免了類型強轉的麻煩。
再看一個例子:
package untl2; import java.util.ArrayList; public class MyType {public static void main(String[] args) {ArrayList<String> arrayList1=new ArrayList();ArrayList<Integer> arrayList2=new ArrayList();Class cla1=arrayList1.getClass();Class cla2=arrayList2.getClass();if(cla1==cla2){System.out.println("運行后會進行去泛型的操作所以兩者相等");}elseSystem.out.println("運行后不會進行去泛型的操作");} } 運行結果: 運行時會進行去泛型的操作所以兩者相等通過上面的例子可以證明,在編譯之后程序會采取去泛型化的措施。也就是說Java中的泛型,只在編譯階段有效。在編譯過程中,正確檢驗泛型結果后,會將泛型的相關信息擦出,并且在對象進入和離開方法的邊界處添加類型檢查和類型轉換的方法。也就是說,泛型信息不會進入到運行時階段。泛型的本質就是利用編譯器實現的Java語法糖,編譯器將java文件轉換為class文件前,會進行泛型擦除,所以在反編譯的class文件中,是看不到泛型聲明的
是時候搬出我泛型的祖傳定義和概述了:
泛型,即“參數化類型”。一提到參數,最熟悉的就是定義方法時有形參,然后調用此方法時傳遞實參。那么參數化類型怎么理解呢?顧名思義,就是將類型由原來的具體的類型參數化,類似于方法中的變量參數,此時類型也定義成參數形式(可以稱之為類型形參),然后在使用/調用時傳入具體的類型(類型實參)。
泛型的本質是為了參數化類型(在不創建新的類型的情況下,通過泛型指定的不同類型來控制形參具體限制的類型)。也就是說在泛型使用過程中,操作的數據類型被指定為一個參數,這種參數類型可以用在類、接口和方法中,分別被稱為泛型類、泛型接口、泛型方法。
2.泛型類
2.1.泛型類的定義格式:
class 類名稱 <泛型標識:可以隨便寫任意標識號,標識指定的泛型的類型>{private 泛型標識 /*(成員變量類型)*/ value; .....} }2.2.泛型類的具體實例:
我們可以看一下我們API中的ArrayList集合源碼:
class ArrayList<E>{ public boolean add(E e){ }public E get(int index){ }.... }當然,我們也可以自定義例如:
但是要注意以下兩點:
1.泛型的類型參數只能是類類型(包括自定義類),不能是簡單類型
2.傳入的實參類型需與泛型的類型參數類型相同
定義的泛型是不需要傳入具體的實參的,什么意思呢,還是上邊這個例子, MyType<String> myType=new MyType(str);我們指定了泛型為String,當然我們也能不指定泛型類型,隨便在構造器中傳入任何類型的數據,編譯器會自動檢測
3.泛型接口
3.1.泛型接口的定義格式:
public interface Generator<T> {public T next(); }3.2.泛型接口的實現類未傳入泛型實參:
例如:
package untl2; public interface MyType<T> {public T fly(); } class person implements MyType{}那么我們知道實現接口肯定要重寫里邊的抽象方法,但是接口的泛型你不明確,所以重寫方法的返回值類型也不明確,所以我們可以使用Object作為方法的反回值來解決:
package untl2; public interface MyType<T> {public T fly(); } class person implements MyType{public Object fly(){System.out.println("讓我飛");return null;}public static void main(String[] args) {person p=new person();p.fly();} } 運行結果: 讓我飛3.3.泛型接口的實現類傳入泛型實參:
分為兩種情況,
(1)第一種是實現接口不帶泛型
(2)第二種是兩者都帶泛型:
package untl2; public interface MyType<T> {public T fly(); } class person<T> implements MyType<T>{public T fly(){System.out.println("讓我飛");return null;}public static void main(String[] args) {person<String> p=new person();p.fly();} } 運行結果: 我要飛我們必須注意 實現接口所帶泛型必須要抽象方法的泛型參數一樣
要不然:
package untl2; public interface MyType<T> {public T fly(); } class person<E> implements MyType<T>{public E fly(){System.out.println("讓我飛");return null;}public static void main(String[] args) {person<String> p=new person();p.fly();} } 編譯報錯3.4.泛型接口傳入實參:
package untl2; public interface MyType<T> {public T fly(); } class person implements MyType<String>{public String fly(){System.out.println("讓我飛");return null;}public static void main(String[] args) {person p=new person();p.fly();} }4.泛型方法
泛型類,是在實例化類的時候指明泛型的具體類型;泛型方法,是在調用方法的時候指明泛型的具體類型
4.1.泛型方法的定義格式:
修飾符 <泛型> 返回值類型(可以使用泛型) 方法名(參數列表(可以使用泛型)){方法體;}4.2.泛型方法的簡單使用及代碼分析:
package untl2;public class MyThroad<T>{private T key;public MyThroad(T t){this.key=t;}public T getKey(){return key;}public <E> E func(T t){System.out.println("我很開心");return null;} // public <E> E vunc(MyThroad<K> k ) // { // // }public <E,K> E vunc(MyThroad<K> k ){return null;}public static void main(String[] args) {}}分析之前請先好好看看這一句話:
泛型類,是在實例化類的時候指明泛型的具體類型;泛型方法,是在調用方法的時候指明泛型的具體類型
首先getkey()不是一個泛型方法
1.從泛型確定類型的時間:這只是類中一個普通的成員方法,只不過他的返回值是在聲明泛型類已經聲明過的泛型。在你實例化對象的時候就會指定T的類型,并不是調用的時候確定的
2.格式上:與泛型方法的定義格式不一致
func方法是一個泛型方法
被注釋的vunc()方法雖然我們聲明了,也表明了這是一個可以處理泛型的類型的泛型方法。
但是只聲明了泛型類型T,并未聲明泛型類型K,因此編譯器并不知道該如何處理E這個類型。
注:在泛型方法里邊定義的泛型要不是在調用方法前就確定了類型,要不就要在尖括號里邊聲明
4.3.泛型類中的泛型方法:
這里用內部類的例子:
package untl; public class GenericFruit {class Fruit{@Overridepublic String toString() {return "fruit";}}class Apple extends Fruit{@Overridepublic String toString() {return "apple";}}class Person{@Overridepublic String toString() {return "Person";}}class GenerateTest<T>{public void show_1(T t){System.out.println(t.toString());}//在泛型類中聲明了一個泛型方法,使用泛型E,///這種泛型E可以為任意類型。可以類型與T相同,也可以不同。//由于泛型方法在聲明的時候會聲明泛型<E>,//因此即使在泛型類中并未聲明泛型,編譯器也能夠正確識別泛型方法中識別的泛型。public <E> void show_3(E t){System.out.println(t.toString());}//在泛型類中聲明了一個泛型方法,使用泛型T,//注意這個T是一種全新的類型,可以與泛型類中聲明的T不是同一種類型。public <T> void show_2(T t){System.out.println(t.toString());}}public static void main(String[] args) {GenericFruit frult=new GenericFruit();Apple apple =frult.new Apple();Person person = frult.new Person();GenerateTest<Fruit> generateTest = frult.new GenerateTest<Fruit>();generateTest.show_1(apple);//generateTest.show_1(person);//使用這兩個方法都可以成功generateTest.show_2(apple);generateTest.show_2(person);generateTest.show_3(apple);generateTest.show_3(person);} } 運行結果: apple apple Person apple Person4.3.泛型方法與可變參數:
package untl; public class GenericFruit {public <T> void printf(T...d){for (T t:d){System.out.println(t);}}public static void main(String[] args) {GenericFruit p=new GenericFruit();p.printf("張三",123,88.88,true);}} 運行結果: 張三 123 88.88 true4.4.靜態方法與泛型:
如果靜態方法要使用泛型的話,必須將靜態方法也定義成泛型方法 。
如果靜態方法使用泛型,但又不是泛型方法:
編譯就會不通過
但是,聲明成泛型方法就會解決這個問題
5.泛型通配符
5.1.泛型通配符’?’
?:代表任意的數據類型
使用方式:
1.不能創建對象使用
2.只能作為方法使用
看下面一段代碼:
明顯的list2不是Interger類型,所以編譯會報錯,但是如果我就想使用print方法遍歷任意泛型的ArrayList集合,這個時候就可以使用我們的通配符
一定注意通配符必須在定義的時候不能用泛型通配符如:ArrayList<?> list2=new ArrayList<?>();就會報錯,但是作為參數傳遞的時候可以用,就比如以上的例子
5.2.泛型通配符的高級使用—受限泛型:
在使用泛型的時候,我們還可以為傳入的泛型類型實參進行上下邊界的限制,如:類型實參只準傳入某種類型的父類或某種類型的子類。
泛型的上下限:
1.上限格式:類型名稱 <? extends 類> 對象名稱,意義:只能接受該類型及其子類
2.下限格式:類型名稱 <? super 類> 對象名稱,意義:只能接受此類型及其父類、
看下面這個例子:
首先我們知道Number類是Integer的父類
6.泛型數組
在java中是”不能創建一個確切的泛型類型的數組”的。
也就是說下面這段代碼是報錯的:
List<String>[] ls = new ArrayList<String>[10];這樣也是報錯的:
List ls = new ArrayList<String>[10];但是而使用通配符創建泛型數組是可以的,如下面這個例子:
List<?>[] ls = new ArrayList<?>[10];這樣也是可以的:
List<String>[] ls = new ArrayList[10];總結
以上是生活随笔為你收集整理的从0到掌握Java泛型有这一篇博客就够了的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java注解原来如此通俗易懂
- 下一篇: Java枚举(深刻而不深沉平淡而不平庸)