泛型
爲什麼要有泛型?
集合容器類在設計階段/聲明階段不能確定這個容器到底實際存的是什麼類型的對象,
所以在JDK1.5之前只能把元素類型設計爲Object,JDK1.5之後使用泛型來解決。
因爲這個時候除了元素的類型不確定,其他的部分是確定的,例如關於這個元素如何
保存,如何管理等是確定的,因此此時把元素的類設計成一個參數,這個類型參數
叫做泛型。
什麼是泛型
所謂泛型,就是允許在定義類、接口時通過一個標識表示類中某個屬性的類
型或者是某個方法的返回值及參數類型。這個類型參數將在使用時( 例如,
繼承或實現這個接口,用這個類型聲明變量、創建對象時)確定(即傳入實
際的類型參數,也稱爲類型實參)。Collection<E>, List<E>,
ArrayList<E> 這個<E>就是類型參數,即泛型。
泛型類
class Myclass<T> {
//T代表類型參數,指代任何類型(不包括基本類型8種,要使用其包裝類),常用單個大寫字母表示,
private T t;
}
public class Test1 {
public static void main(String[] args) {
Myclass<String> myclass1=new Myclass<String>();
Myclass<Integer> myclass2=new Myclass<Integer>();
}
}
- 泛型類可以接受多個類型參數
class Mclass<T,E> {
private T t;
private E e;
}
public class Test1 {
public static void main(String[] args) {
Mclass<String,Integer> mclass=new Mclass<String, Integer>();
}
}
- 引入泛型後,一個泛型類的類型在使用時已經確定好,因此無需向下轉型
泛型類的使用-通配符(Wildcards)
? 用於在泛型的使用,即爲通配符
public class MyArrayList<E> {...}
// 可以傳入任意類型的 MyArrayList
public static void printAll(MyArrayList<?> list) {
...
} /
/ 以下調用都是正確的
printAll(new MyArrayList<String>());
printAll(new MyArrayList<Integer>());
printAll(new MyArrayList<Double>());
printAll(new MyArrayList<Number>());
printAll(new MyArrayList<Object>());
- 通配符-上界
<? extends 上界>
// 可以傳入類型實參是 Number 子類的任意類型的 MyArrayList
public static void printAll(MyArrayList<? extends Number> list) {
...
}
- 通配符-下界
<? super 下界>
// 可以傳入類型實參是 Integer 父類的任意類型的 MyArrayList
public static void printAll(MyArrayList<? super Integer> list) {
...
}
泛型方法
在方法聲明時用<T>表示的方法,<T>中的T稱爲類型參數,而方法中的T被稱爲參數
化類型,他不是運行時真正的參數
public <T> void fun(T t) {
System.out.println(t);
}
public <T> T test(T t) { //<T>泛型方法的定義,表示該方法爲泛型方法。第二個T表示返回類型是T,第三個T表示參數類型是T.
return t;//類型參數也可以做返回值
}
- 當泛型類與泛型方法共存時,泛型方法始終與自己定義的類型爲準、定義時讓泛型類和泛型方法的類型不同名,來避免混淆
class Myclass2<T> {
public <T> T func(T t) {
return t;
}
}
public class Test1 {
public static void main(String[] args) {
Myclass2<String> myclass2=new Myclass2<>();
System.out.println(myclass2.func(12));
System.out.println(myclass2.func("123"));
}
}
泛型接口
interface ISubject<T> {
void fun(T t);
}
- 子類在實現接口時有兩種實現方式
1)此時子類實現接口時就確定類型
class SujectImpl implements ISubject<String> {
@Override
public void fun(String s) {
}
}
2)子類實現接口時仍然保留泛型
class SubjectImpl2<T> implements ISubject<T> {
@Override
public void fun(T t) {
}
}
類型擦除、
泛型是作用在編譯期間的一種機制,實際上運行期間是沒有這麼多類的,那運行期間
是什麼類型呢?這裏就是類型擦除在做的事情
jdk 1.5引入 泛型只存在於編譯階段,在進入JVM之前,與泛型有關的信息會被完全擦除 泛型類再進行類型擦除時,未指定泛型的上限,泛型相關信息會被擦除位Object類型,如果有上限,擦除爲對應類型的上限。
class Myclass<T,E extends Number> {
public T t;
public E e;
}
T>>>Object
E>>>Number
泛型的注意點
- 泛型類型參數不支持基本數據類型
- 無法實例化泛型類型的對象
- 無法使用泛型類型聲明靜態的屬性
- 無法使用 instanceof 判斷帶類型參數的泛型類型
- 無法創建泛型類數組
- 無法 create、catch、throw 一個泛型類異常(異常不支持泛型)
- 泛型類型不是形參一部分,無法重載
- 泛型代碼與JVM ① 虛擬機中沒有泛型,只有普通類和方法。 ② 在編譯階段,所有泛型類的類型參數都會被Object或者它們的限定邊界來替換。(類型擦除) ③ 在繼承泛型類型的時候,橋方法的合成是爲了避免類型變量擦除所帶來的多態災難。 無論我們如何定義一個泛型類型,相應的都會有一個原始類型被自動提供。原始類型的名字就是擦除類型參數的泛型類型的名字。