千鋒教育Java入門全套視頻教程(java核心技術,適合java零基礎,Java

泛型
1、泛型簡述
JDK1.4以前:
1、 裝入集合的數(shù)據(jù)都會被當作Object對象來存放,從而失去了自己的實際類型。
2、 從集合中取出元素時,需要進行強制類型轉換。效率低,容易產(chǎn)生錯誤。
從JDK1.5開始:
sun公司推出了泛型來解決上述問題
1、在定義一個集合時就指定集合存儲的對象的數(shù)據(jù)類型
Collection<String> coll = new ArrayList<String>();
3、 存入數(shù)據(jù)時只能存入泛型指定的數(shù)據(jù)類型,如果存入其他數(shù)據(jù)類型,則會編譯錯誤,避數(shù)據(jù)存入時的問題。
coll.add(new Integer(1));?//**編譯錯誤!**
3、從集合中取出元素時,無需轉型了。
如:String str1 = it.next();
優(yōu)點
1、簡化集合的使用
2、增強代碼的可讀性和穩(wěn)定性
2、泛型的使用
使用場合
?1.非靜態(tài)的成員屬性類型 ?2.非靜態(tài)方法的形參類型(包括非靜態(tài)成員方法和構造器) ?3.非靜態(tài)的成員方法的返回值類型
泛型標識
一般來說泛型標識是可以任意設置的
?T :代表一般的任何類。 ?E :代表 Element 元素的意思,或者 Exception 異常的意思。 ?K :代表 Key 的意思。 ?V :代表 Value 的意思,通常與 K 一起配合使用。 ?S :代表 Subtype 的意思,文章后面部分會講解示意。
泛型接口
public interface IFan<T> { // t 這個參數(shù)的數(shù)據(jù)類型為 T, T 的類型由外部傳入 ?default void fun(T t){ ???System.out.println(); ?} }
定義一個接口 IA 繼承了 泛型接口 IFan,在 接口 IA 定義時必須確定泛型接口 IFan中的類型參數(shù)。
定義一個類 Sub 實現(xiàn)了 泛型接口 IUsb,在 類 Sub 定義時需要確定泛型接口 IFan中的類型參數(shù)。
public interface IFan<T> {} //1、確定接口的實際類型 class Sub implements IFan<String>{} interface Test extends IFan<String>{} //2、在子類中也添加泛型,并且和父類的泛型相同 class Sub<T> implements IFan<T>{} interface Test<T> extends IFan<T>{}
因為子類會繼承父類的方法和屬性,調用子類時如果沒有確定類型或者子類沒有添加泛型,則無法將參數(shù)類型傳給IFan接口,就找不到類型來替代T
泛型類
public class Fan<T> { ?// t 這個成員變量的數(shù)據(jù)類型為 T, T 的類型由外部傳入? ?private T t; // 泛型構造方法形參 t 的類型也為 T,T 的類型由外部傳入 ?public Fan(T t) { ???this.t = t; ?} ?? // 泛型方法 getT 的返回值類型為 T,T 的類型由外部指定 ?public T getT(){ ???return t; ?} }
泛型類中的靜態(tài)方法和靜態(tài)變量不可以使用泛型類所聲明的類型參數(shù)
- 泛型類中的類型參數(shù)的確定是在創(chuàng)建泛型類對象的時候(例如 ArrayList< Integer >)。
- 而靜態(tài)變量和靜態(tài)方法在類加載時已經(jīng)初始化,直接使用類名調用;在泛型類的類型參數(shù)未確定時,靜態(tài)成員有可能被調用,因此泛型類的類型參數(shù)是不能在靜態(tài)成員中使用的。
泛型類不只接受一個類型參數(shù),它還可以接受多個類型參數(shù)。
public class Fan <E,T>{}
泛型限定方法
當在一個方法簽名中的返回值前面聲明了一個 < T > 時,該方法就被聲明為一個泛型方法。< T >表明該方法聲明了一個類型參數(shù) T,并且這個類型參數(shù) T 只能在該方法中使用。當然,泛型方法中也可以使用泛型類中定義的泛型參數(shù)。
public <類型參數(shù)> 返回類型 方法名(類型參數(shù) 變量名) { ?... }
只有在方法簽名中聲明了< T >的方法才是泛型方法,僅使用了泛型類定義的類型參數(shù)的方法并不是泛型方法。
public class Test<U> { // 該方法只是使用了泛型類定義的類型參數(shù),不是泛型方法 public void testMethod(U u){ System.out.println(u); } // <T> 真正聲明了下面的方法是一個泛型方法 public <T> T testMethod1(T t){ return t; } }
泛型方法中可以同時聲明多個類型參數(shù)。
public class TestMethod<U> { public <T, S> T testMethod(T t, S s) { return null; } }
泛型方法中也可以使用泛型類中定義的泛型參數(shù)。
public class TestMethod<U> { public <T> U testMethod(T t, U u) { return u; } }
特別注意的是:泛型類中定義的類型參數(shù)和泛型方法中定義的類型參數(shù)是相互獨立的,它們一點關系都沒有。
public class Test<T> { public void testMethod(T t) { System.out.println(t); } public <T> T testMethod1(T t) { return t; } }
上面代碼中,Test< T > 是泛型類,testMethod() 是泛型類中的普通方法,其使用的類型參數(shù)是與泛型類中定義的類型參數(shù)。
而 testMethod1() 是一個泛型方法,他使用的類型參數(shù)是方法簽名中聲明的類型參數(shù)。
雖然泛型類中定義的類型參數(shù)標識和泛型方法中定義的類型參數(shù)標識都為< T >,但它們彼此之間是相互獨立的。也就是說,泛型方法始終以自己聲明的類型參數(shù)為準。
- < T >表明該方法聲明了一個類型參數(shù) T,并且這個類型參數(shù) T 只能在該方法中使用。
- 為了避免混淆,如果在一個泛型類中存在泛型方法,那么兩者的類型參數(shù)最好不要同名。
- 與泛型類的類型參數(shù)定義一樣,此處泛型方法中的 T 可以寫為
任意標識
,常見的如 T、E、K、V 等形式的參數(shù)常用于表示泛型。
泛型方法簽名中聲明的類型參數(shù)只能在該方法里使用,而泛型接口、泛型類中聲明的類型參數(shù)則可以在整個接口、類中使用。當調用泛型方法時,根據(jù)外部傳入的實際對象的數(shù)據(jù)類型,編譯器就可以判斷出類型參數(shù) T所代表的具體數(shù)據(jù)類型。
3、泛型可擦除性
java文件源碼
List<Integer> list = new ArrayList<>();
反編譯后的java代碼
java.util.List list = new ArrayList();
明顯的把泛型<Integet>給擦除了
既然java的泛型僅存在于源碼,編譯后就被擦除,那泛型是怎么起作用的呢
裝箱
編譯前:
list.add(1); list.add(2); list.add(3); list.add(4);
編譯后:
List list = new ArrayList(); list.add(Integer.valueOf(1)); list.add(Integer.valueOf(2)); list.add(Integer.valueOf(3)); list.add(Integer.valueOf(4));
明顯的用了valueOf方法裝箱
泛型不安全
public static void main(String[] args) { ?List<Integer> list = new ArrayList<>(); ?list.add(1); ?list.add(2); ?list.add(3); ?list.add(4); ?fun(list,"str"); ?System.out.println(list); } //定義一個方法,用來實現(xiàn)add方法 //但是接收參數(shù)時不加泛型,因為在編譯后泛型會被擦除,所以這種操作是允許的 public static void fun(List list,Object o){ ?list.add(o); }
結果:
[1, 2, 3, 4, str]
混進去一個String類型的數(shù)據(jù)