為什么需要泛型
泛型利于代碼重用。比如實現(xiàn)針對某一種具體數(shù)據(jù)類型的功能,將具體數(shù)據(jù)類型替換為泛型枢希,則可以實現(xiàn)為針對多種數(shù)據(jù)類型的功能,極大的提高了功能的復用性朱沃。
類型安全苞轿,讓編譯器幫助我們進行類型檢查茅诱。指定泛型中的類型,讓java編譯器幫助我們檢查類型以及類型轉(zhuǎn)換搬卒,不再需要我們自己進行類型判斷及強制裝換瑟俭。
泛型的使用
//動物類
public class Animal {
public void eat(){
System.out.println("動物吃");
}
}
//貓類
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("貓吃東西");
}
public void cry(){
System.out.println("喵喵叫");
}
}
//卡菲貓
public class Garfield extends Cat {
}
//泛型類的使用,這種泛型使用方式會導致類型被擦除為所有類的父類Object
//這意味著你在運行期會丟失原本類型的所有信息契邀,無法使用原本類相關(guān)的任何屬性和方法
public class GenericClass<T> {
//泛型擦除摆寄,查看字節(jié)碼data的類型為 Ljava/lang/Object; data
private T data;
GenericClass(T data){
this.data = data;
}
public T getData(){
return data;
}
public void setData(T data){
this.data = data;
}
}
//使用了限制的泛型,通過這種方式可以一定程度上彌補第一種方式的弊端
//因為泛型類型被擦除為指定父類坯门,這樣可以使用父類的方法
public class GenericExtendsClass<T extends Animal> {
//泛型擦除為Animal微饥,查看字節(jié)碼data的類型為 L easycode/Animal; data
private T data;
GenericExtendsClass(T data){
this.data = data;
}
public T getData(){
return data;
}
public void setData(T data){
this.data = data;
}
public void doOtherSomething(){
//調(diào)用了Animal特有的方法
data.eat();
}
}
//泛型方法的使用,只能定義靜態(tài)泛型方法
private static <T> T genericAdd(T a, T b) {
System.out.println(a + "+" + b + "="+a+b);
return a;
}
java泛型的本質(zhì)
c++的泛型是根據(jù)模板類生成不同的類達到泛型的目的古戴,但是java的泛型并不是如此的欠橘,java是通過泛型擦除的方式來實現(xiàn)泛型,所以java的泛型也被稱為偽泛型现恼,即只存在于編譯期的泛型肃续,在運行階段找不到泛型信息。
所以叉袍,可以將java的泛型當做是一種類型檢查的手段始锚,而不要像c++一樣將泛型作為實際的類型來使用。
Java泛型中的約束
泛型不能實例化畦韭。因為java的泛型擦除疼蛾,所以在運行期不知道泛型信息,自然也就無法實例化艺配。
類或接口的靜態(tài)域中不能引用泛型類型變量察郁,但是可以定義靜態(tài)泛型方法。
基本數(shù)據(jù)類型無法作為泛型類型转唉。因為泛型擦除其實就是將子類賦值給父類皮钠,所以基本數(shù)據(jù)類型無法作為泛型類型。
無法使用 instanceof 關(guān)鍵字或者 ==運算符 判斷泛型類的類型赠法,需要借助別的方法(Type類型)麦轰,但是對原生數(shù)據(jù)類型無影響
泛型類的原生數(shù)據(jù)類型與所傳遞的泛型無關(guān),無論傳遞什么類型砖织,原生類都是一樣的
-
泛型數(shù)組可以聲明但無法實例化款侵。原因同泛型不能實例化,但是可以實例化非泛型數(shù)組侧纯,然后通過定義變量的泛型來進行類型檢查新锈。
//實例化泛型數(shù)組,編譯不通過 //ArrayList<String>[] listArray = new ArrayList<String>[5]; //實例化非泛型數(shù)組眶熬,編譯通過 //通過給listArray添加泛型定義來進行泛型檢查 ArrayList<String>[] listArray = new ArrayList[5]; //編譯不通過 //listArray[0] = new ArrayList<Integer>(); //編譯通過 listArray[0] = new ArrayList<String>();
泛型類不能繼承Exception或者Throwable
不能捕獲泛型類型限定的異常妹笆,只能將泛型類型限定的異常拋出
泛型通配符
泛型通配符主要用于一些參數(shù)的接收或返回块请。
通配符類型
<? extends Parent>:指定了泛型類的上界,一般用于獲取元素即get first拳缠,因為可以肯定元素為Parent的某一個子類或Patent墩新,但是具體是哪一個類型無法確定,所以無法添加窟坐,只能獲取海渊。
<? super Child>:指定了泛型的下界,一般用于添加元素即put first狸涌,因為可以肯定元素為Child的父類切省,所以可以添加Child類型或者Child的子類,都是滿足多態(tài)存儲的帕胆。
-
<?>:等價于<? extends Object>朝捆,即沒有限制的泛型類型。
<?>比如 List<?>一般作為參數(shù)來接收外部的集合懒豹,或者返回一個不知道具體元素類型的集合芙盘。因為<?>代表未知類型,所以不允許往里面添加元素脸秽,只能取出來儒老。比如public List<?> getList(){ return new ArrayList<String>(); }
//測試 extends 和 super
List<Animal> animal = new ArrayList<Animal>();
ArrayList<Cat> cat = new ArrayList<>();
ArrayList<Garfield> garfield = new ArrayList<>();
animal.add(new Animal());
cat.add(new Cat());
garfield.add(new Garfield());
//測試賦值操作
//編譯出錯,Animal是Cat的父類记餐,不能通過泛型檢測
// List<? extends Cat> extendsCatFromAnimal = animal;
List<? super Cat> superCatFromAnimal = animal;
List<? extends Cat> extendsCatFromCat = cat;
List<? super Cat> superCatFromCat = cat;
List<? extends Cat> extendsCatFromCarfield = garfield;
//編譯出錯 Garfield是 Cat的子類驮樊,不能通過泛型檢測
// List<? super Cat> superCatFromCarfield = carfield;
//測試add方法
//編譯出錯,extends只能獲取元素而不能添加元素
// extendsCatFromCat.add(new Animal());
// extendsCatFromCat.add(new Cat());
// extendsCatFromCat.add(new Garfield());
//編譯出錯片酝,可以添加Cat及其子類囚衔,但是不能添加其父類Animal
// superCatFromCat.add(new Animal());
superCatFromCat.add(new Cat());
superCatFromCat.add(new Garfield());
//測試get方法
Object catExtends2 = extendsCatFromCat.get(0);
Cat catExtends1 = extendsCatFromCat.get(0);
// Garfield garfield1 = extendsCatFromCarfield.get(0);
//可以確定Object是所有類的父類
Object catSuper1 = superCatFromCat.get(0);
Object catSuper2 = superCatFromAnimal.get(0);
如何獲取泛型的參數(shù)類型
獲取泛型的類型參數(shù)類型需要用到Type接口以及它的子接口。
獲取泛型的類型參數(shù)類型需要用到Type接口以及它的子接口雕沿。
Type類型的子接口有ParameterizedType练湿、GenericArrayType、TypeVariable审轮、WildcardType和Class實現(xiàn)類肥哎。
public interface ParameterizedType extends Type {
//private List<String> list;
//獲取<>中的數(shù)據(jù)類型,即String
Type[] getActualTypeArguments();
//獲取<>前面的實際類型疾渣,即List
Type getRawType();
//如果這個類型是某個類型的所屬篡诽,獲得這個所有者類型,否則返回null
Type getOwnerType();
}
public interface GenericArrayType extends Type {
// T[]
//獲取數(shù)組元素類型榴捡,即 T
Type getGenericComponentType();
}
public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {
//private T t;
//獲取泛型的上界杈女,可以通過extends通配符指定,默認是Object
Type[] getBounds();
//獲取聲明該類型變量實體,即該變量所在的類、方法等
D getGenericDeclaration();
//獲得名稱碧信,即K、V街夭、E
String getName();
}
public interface WildcardType extends Type {
//獲取泛型表達式上界
Type[] getUpperBounds();
//獲取泛型表達式下界
Type[] getLowerBounds();
}
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement {
Type接口時是Java編程語言中所有類型的公共高級接口砰碴,其中的所有類型如下所示:
原始類型(一般的java類包括枚舉、注解板丽、非泛型數(shù)組)呈枉,對應 Class 類
參數(shù)化類型(List<String>、Map<String, Object>這種形式)埃碱,對應 ParameterizedType 接口
數(shù)組類型(T[])猖辫,對應 GenericArrayType 接口
類型變量(T),對應 TypeVariable 接口
基本類型砚殿,對應Class對象
舉個例子(獲取泛型類型)
public class GenericTest {
static class Example{
//參數(shù)化類型
private List<String> list;
}
@Test
public void test() throws Exception{
Class<Example> exampleClass = Example.class;
Field field = exampleClass.getDeclaredField("list");
Type type = field.getGenericType();
Assert.assertTrue(type instanceof ParameterizedType);
ParameterizedType parameterizedType = (ParameterizedType)type;
Assert.assertEquals(String.class ,parameterizedType.getActualTypeArguments()[0]);
Assert.assertEquals(List.class, parameterizedType.getRawType());
}
}
參考如下文章:
Java泛型
Type類型常用API介紹
Type類型Demo