泛型是JDK 1.5的一項新特性,它的本質是參數化類型(Parameterized Type),即所操做的數據類型在定義時被指定爲一個參數。當咱們使用的時候給這個參數指定不一樣的對象類型,就能夠處理不一樣的對象。這種參數類型能夠用在類、接口和方法的建立中,分別稱爲泛型類、泛型接口和泛型方法。java
泛型聲明方式:<佔位符>
佔位符有兩大類:安全
泛型類和普通類的區別就是類定義時,在類名後加上泛型聲明。泛型類的內部成員、方法就可使用聲明的參數類型。泛型類最多見的用途就是做爲容納不一樣數據類型的容器類,好比Java集合容器類。ide
class GenericClass<T>{ private T value; public T getValue() { return value; } public void setValue(T value) { this.value = value; } }
和泛型類同樣,泛型接口在接口名後添加泛型聲明,接口方法就能夠直接使用聲明的參數類型。
實現類在實現泛型接口時須要指明具體的參數類型,否則默認類型是 Object,這就失去了泛型接口的意義。this
interface genericInterface<K, V> { K getKey(); V getValue(); }
未指明類型的實現類,默認是 Object 類型:code
class GenericClass implements genericInterface{ @Override public Object getKey() { return null; } @Override public Object getValue() { return null; } }
指明瞭類型的實現:對象
class GenericClass implements genericInterface<String, Object> { @Override public String getKey() { return null; } @Override public Object getValue() { return null; } }
泛型方法指的是使用泛型的方法。若是這個方法所在的類是個泛型類,直接使用類聲明的參數類型。接口
若是這個方法所在的類不是泛型類或者他想要處理不一樣於泛型類所聲明的參數類型,這時候咱們就須要使用泛型方法。即在方法前加入泛型聲明,方法就能夠直接使用聲明的參數類型。字符串
class GenericClass { private Object value; public <T> T getValue(){ return (T)value; } public void setValue(Object value){ this.value = value; } }
方法getValue()就是泛型方法,調用代碼以下:get
GenericClass genericClass = new GenericClass(); genericClass.setValue(123); Integer value = genericClass.getValue();
泛型的主要目的經過參數類型檢查,提升JAVA程序數據類型的安全,提前發現錯誤,避免ClassCastException 異常發生。編譯器
List list = new ArrayList(); list.add("test generic"); list.add(123);// 注:123會轉換成Integer對象 list.add(null);
當不實用泛型的時候,上面代碼的使用是被容許的,咱們能夠放入任何對象,當咱們按照某種對象類型去取數據就會出現ClassCastException 異常。
//List<String> list = new ArrayList<String>(); List<String> list = new ArrayList(); list.add("test generic"); //list.add(123); list.add(null);
List是泛型接口,當咱們這樣使用的時候,「123」這個元素是不被容許使用的。
當咱們不使用泛型時,每次取出集合中的元素都須要咱們強制轉換成咱們須要的元素。(Object->Class Type)。
List list = new ArrayList(); list.add("test generic"); String s = (String) list.get(0);
「test generic」存入list是一個Object的對象,取出來是咱們須要轉換成咱們要的字符串String類型。
List<String> list = new ArrayList(); list.add("test generic"); String s = list.get(0);
使用泛型後list.get(0)中取出來的數據類型,就是咱們存入時的String類型。
經過泛型的應用,作到了簡碼,並且JVM中類文件也相應的減小。JVM幾乎沒有作任何的更改,全部的類型校驗,類型轉換都是在編譯器階段完成。
定義時不關心或者不肯定實際要操做的數據是什麼類型,可使用無限制通配符(尖括號裏一個問號,即 <?> ),表示能夠持有任何類型。
class GenericClass<T> { private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } @Override public String toString() { return "GenericClass{" + "data=" + data + '}'; } }
泛型類定義
public static void print(GenericClass<?> context){ System.out.println(context); }
通配符定義的方法參數類型
GenericClass<Integer> integerGenericClass = new GenericClass<>(); integerGenericClass.setData(123); GenericClass<String> stringGenericClass = new GenericClass<>(); stringGenericClass.setData("123"); print(integerGenericClass); print(stringGenericClass);
使用時?是一個萬能的類型,T只能是具體的類型
在類型參數中使用extends表示這個泛型中的參數必須是 E 或者 E 的子類,這樣有兩個好處:
List<? extends Number> list = new ArrayList<Number>(){{add(new Integer(456));add(new Long(456L)); }}; list.add(null); //list.add(new Integer(123)); //list.add(new Long(123L)); System.out.println(list.size()); Number number = list.get(0);
當使用上邊界通配符時,是不容許往裏存數據的,不肯定具體元素類型。
在類型參數中使用super表示這個泛型中的參數必須是E或者E的父類。
用於靈活寫入或比較,使得對象能夠寫入父類型的容器,使得父類型的比較方法能夠應用於子類對象。
List<? super C> list = new ArrayList<B>(){{add(new B()); add(new C());}}; list.add(null); list.add(new C()); list.add(new B()); //list.add(new A()); Object object = list.get(0);
C是B的子類,B是A的子類。當使用下界通配符時,是容許存放E和E的子對象數據的,由於子類對象的引用能夠賦值給父類對象的引用(頂級父類是Object),因此取出來的數據類型是Object.