泛型的定義以及存在意義
泛型,即“引數化型別”,就是將型別由原來的具體的型別引數化,類似于方法中的變數引數,此時型別也定義成引數形式(可以稱之為型別形參),然后在使用/呼叫時傳入具體的型別(型別實參),
例如:GenericClass<T>{}
使用泛型的主要優點是能夠在編譯時而不是在運行時檢測錯誤,
泛型只在編譯階段有效,看下面的代碼:
List<String> stringArrayList = new ArrayList<String>(); List<Integer> integerArrayList = new ArrayList<Integer>(); Class classStringArrayList = stringArrayList.getClass(); Class classIntegerArrayList = integerArrayList.getClass(); if(classStringArrayList.equals(classIntegerArrayList)){ Log.d("泛型測驗","型別相同"); }
輸出結果:D/泛型測驗: 型別相同,
通過上面的例子可以證明,在編譯之后程式會采取去泛型化的措施,也就是說Java中的泛型,只在編譯階段有效,在編譯程序中,正確檢驗泛型結果后,會將泛型的相關資訊擦出,并且在物件進入和離開方法的邊界處添加型別檢查和型別轉換的方法,也就是說,泛型資訊不會進入到運行時階段,
對此總結成一句話:泛型型別在邏輯上看以看成是多個不同的型別,實際上都是相同的基本型別,
Generic generic = new Generic("111111");
Generic generic1 = new Generic(4444);
Generic generic2 = new Generic(55.55);
Generic generic3 = new Generic(false);
Log.d("泛型測驗","key is " + generic.getKey());
Log.d("泛型測驗","key is " + generic1.getKey());
Log.d("泛型測驗","key is " + generic2.getKey());
Log.d("泛型測驗","key is " + generic3.getKey());
D/泛型測驗: key is 111111
D/泛型測驗: key is 4444
D/泛型測驗: key is 55.55
D/泛型測驗: key is false
命名型別引數
推薦的命名約定是使用大寫的單個字母名稱作為型別引數,這與 C++ 約定有所不同(參閱 附錄 A:與 C++ 模板的比較),并反映了大多數泛型類將具有少量型別引數的假定,對于常見的泛型模式,推薦的名稱是:
K —— 鍵,比如映射的鍵,
V —— 值,比如 List 和 Set 的內容,或者 Map 中的值,
E —— 例外類,
T —— 泛型,
泛型有三種常用的使用方式:泛型類,泛型介面和泛型方法,
泛型類
一個泛型類(generic class)就是具有一個或多個型別變數的類,
下文寫了一個用泛型定義的堆疊
為了創建一個字串堆找,可以使用new GenericStack<String>() 或new GenericStack<>() ,這可能會誤導你認為GenericStack 的構造方法應該定義為 public GenericStack\<E>()這是錯誤的,它應該被定義為 public GenericStack()
public class GenericStack<E>{
private java.util.ArrayList<E> list=new java.util.ArrayList<>();
public int getSize(){
return list.size;
}
public T peek(){
return list.get(getList()-1);
}
public void push(E o){
list.add(o);
}
public E pop(){
E o = list.get(getList()-1);
list.remove(getSize()-1);
return o;
}
public boolean isEmpty(){
return list.isEmpty();
}
@Overide
public String toString(){
return "stack: "+list.toString();
}
}
同一種泛型可以對應多個版本(因為引數型別是不確定的),不同版本的泛型類實體是不兼容的,
泛型方法
泛型類,是在實體化類的時候指明泛型的具體型別;泛型方法,是在呼叫方法的時候指明泛型的具體型別 ,
/**
* 泛型方法的基本介紹
* @param tClass 傳入的泛型實參
* @return T 回傳值為T型別
* 說明:
* 1)public 與 回傳值中間<T>非常重要,可以理解為宣告此方法為泛型方法,
* 2)只有宣告了<T>的方法才是泛型方法,泛型類中的使用了泛型的成員方法并不是泛型方法,
* 3)<T>表明該方法將使用泛型型別T,此時才可以在方法中使用泛型型別T,
* 4)與泛型類的定義一樣,此處T可以隨便寫為任意標識,常見的如T、E、K、V等形式的引數常用于表示泛型,
*/
public <T> T genericMethod(Class<T> tClass)throws InstantiationException ,
IllegalAccessException{
T instance = tClass.newInstance();
return instance;
}
泛型方法的基本用法
public class GenericTest {
//這個類是個泛型類,在上面已經介紹過
public class Generic<T>{
private T key;
public Generic(T key) {
this.key = key;
}
//我想說的其實是這個,雖然在方法中使用了泛型,但是這并不是一個泛型方法,
//這只是類中一個普通的成員方法,只不過他的回傳值是在宣告泛型類已經宣告過的泛型,
//所以在這個方法中才可以繼續使用 T 這個泛型,
public T getKey(){
return key;
}
/**
* 這個方法顯然是有問題的,在編譯器會給我們提示這樣的錯誤資訊"cannot reslove symbol E"
* 因為在類的宣告中并未宣告泛型E,所以在使用E做形參和回傳值型別時,編譯器會無法識別,
public E setKey(E key){
this.key = keu
}
*/
}
/**
* 這才是一個真正的泛型方法,
* 首先在public與回傳值之間的<T>必不可少,這表明這是一個泛型方法,并且宣告了一個泛型T
* 這個T可以出現在這個泛型方法的任意位置.
* 泛型的數量也可以為任意多個
* 如:public <T,K> K showKeyName(Generic<T> container){
* ...
* }
*/
public <T> T showKeyName(Generic<T> container){
System.out.println("container key :" + container.getKey());
//當然這個例子舉的不太合適,只是為了說明泛型方法的特性,
T test = container.getKey();
return test;
}
//這也不是一個泛型方法,這就是一個普通的方法,只是使用了Generic<Number>這個泛型類做形參而已,
public void showKeyValue1(Generic<Number> obj){
Log.d("泛型測驗","key value is " + obj.getKey());
}
//這也不是一個泛型方法,這也是一個普通的方法,只不過使用了泛型通配符?
//同時這也印證了泛型通配符章節所描述的,?是一種型別實參,可以看做為Number等所有類的父類
public void showKeyValue2(Generic<?> obj){
Log.d("泛型測驗","key value is " + obj.getKey());
}
/**
* 這個方法是有問題的,編譯器會為我們提示錯誤資訊:"UnKnown class 'E' "
* 雖然我們宣告了<T>,也表明了這是一個可以處理泛型的型別的泛型方法,
* 但是只宣告了泛型型別T,并未宣告泛型型別E,因此編譯器并不知道該如何處理E這個型別,
public <T> T showKeyName(Generic<E> container){
...
}
*/
/**
* 這個方法也是有問題的,編譯器會為我們提示錯誤資訊:"UnKnown class 'T' "
* 對于編譯器來說T這個型別并未專案中宣告過,因此編譯也不知道該如何編譯這個類,
* 所以這也不是一個正確的泛型方法宣告,
public void showkey(T genericObj){
}
*/
public static void main(String[] args) {
}
}
宣告泛型方法,將泛型型別<E>置于方法頭中關鍵字static中,例如:
public static <E> void print(E[] list)
為了呼叫泛型,需要將實際型別放在尖括號內作為方法名的前綴,例如:
GenericMethodDemo.<Integer>print(integers);
GenericMethodDemo.<String>print(strings);
示例:對一個物件陣列進行排序
要點提示:可以開發一個泛型方法,對一個Comparable物件陣列進行排序,
創建一個泛型方法,對一個Comparable物件陣列進行排序,這些物件是Comparable介面的實體,使用compareTo()方法進行比較,
public class GenericSort {
public static void show() {
Integer[] intArray = {new Integer(2),new Integer(4),new Integer(3)};
Double[] doubleArray = {new Double(2.5),new Double(6.4),new Double(3.3)};
Character[] charArray = {new Character('a'),new Character('q'),new Character('c')};
String[] stringArray = {"liu","lu","hhh"};
sort(intArray);
sort(doubleArray);
sort(charArray);
sort(stringArray);
System.out.print("sorted integer objects: ");
printList(intArray);
System.out.print("sorted Double objects: ");
printList(doubleArray);
System.out.print("sorted char objects: ");
printList(charArray);
System.out.print("sorted string objects: ");
printList(stringArray);
}
public static <E extends Comparable<E>> void sort(E[] list) { //可以對任何物件型別的陣列進行排序
E currentMin;
int currentMinIndex;
for(int i = 0; i < list.length -1 ;i++) {
currentMin = list[i];
currentMinIndex = i;
for (int j = i+1 ; j < list.length; j++) {
if(currentMin.compareTo(list[j])>0) {
currentMin = list[j];
currentMinIndex = j;
}
}
if(currentMinIndex != i) {
list[currentMinIndex] = list[i];
list[i] = currentMin;
}
}
}
public static void printList(Object[] list) {
for(int i = 0; i< list.length ; i++)
System.out.print(list[i]+" ");
System.out.println();
}
}
泛型型別定義為<E extends Comparable <E>>,這具有兩個含義:
首先,它指定E 是Comparable 的子型別;其次,它還指定進行比較的元素是E 型別的,
通配泛型
通配泛型型別有三種形式一一?、? extends T 或者? super T ,其中T是泛型型別,
第一種形式 ? 稱為 非受限通配 (unbounded wildcard) ,它和? extends Object 是一樣的,表示任何一種物件型別,
public static void print(GenericStack <?> stack)
第二種形式 ? extends T 稱為 受限通配 (bounded wildcard),表示T 或T 的一個子型別,
public static double max(GenericStack <? extends Number> stack)
所以max(new GenericStack <Integer/Double>()) 都是合法的
第三種形式 ? super T 稱為 下限通配 (Iower-bound wildcard) ,表示T 或T 的一個父型別,
GenericStack stack1 = new GenericStack<>(); 一個字串堆疊
GenericStack stack2 = new GenericStack<>(); 一個物件堆疊
如果要呼叫下面的add(stack1,stack2)方法,stack2就應該申明為 <? super T>
public static double max(GenericStack<? extends Number> stack) { // 子型別
double max = stack.pop().doubleValue();
while(!stack.isEmpty()) {
double value = https://www.cnblogs.com/Bluebells/p/stack.pop().doubleValue();
if(value>max)
max = value ;
}
return max;
}
public static void add(GenericStack stack1,
GenericStack<? super T> stack2) {
while(!stack1.isEmpty())
stack2.push(stack1.pop());
}
詳細講解
public class SuperExtneds {
public static void main(String[] args) {
List<? extends Apple> list1 = getExtendsAppleList();
Apple first = list1.get(0);
list1.add(null);
first.sayHello();
//編譯錯誤
//list.add(new Fruit());
//list.add(new Apple())
//list.add(new Object());
List<? super Apple> list2 = getSupperAppleList();
list2.add(new Apple());
list2.add(new HFSApple());
//只能回傳obj
Object item = list2.get(0);
//編譯 錯誤
//Fruit aa1 = list2.get(0);
//Apple aa2 = list2.get(0);
//HFSApple aa3 = list2.get(0);
//Orange aa4 = list2.get(0);
//list2.add(new Fruit());
List<Fruit> list3 = new ArrayList<>();
handlerApple(list3);
list3.get(0).sayHello();
}
public static List<? extends Apple> getExtendsAppleList() {
List<Apple> appleList = new ArrayList<>();
appleList.add(new Apple());
appleList.add(new HFSApple());
//編譯錯誤
//return new ArrayList<Fruit>();
return appleList;
}
public static List<? super Apple> getSupperAppleList() {
List<Fruit> fruitList = new ArrayList<>();
fruitList.add(new Fruit());
fruitList.add(new Orange());
//編譯錯誤
//return new ArrayList<HFSApple>();
return fruitList;
}
public static void handlerApple(List<? super Apple> list) {
list.add(new HFSApple());
list.add(new Apple());
//編譯報錯
//list.add(new Fruit());
//list.add(new Orange());
}
}
class Fruit {
public void sayHello() {
System.out.println("hello");
}
}
class Apple extends Fruit {
}
class HFSApple extends Apple {
}
class Orange extends Fruit {
}
List<? extends Apple> list1 只能獲取某個型別的物件,不能向其中添加Fruit,Orange甚至Apple的實體,這是為什么呢?原來是? expends Apple的含義時Apple或者Apple的子類,這是一個不確定的型別,那么List就不知道到底應該裝入什么型別的物件,所以不允許添加任何型別的物件,但是奇怪的事我們可用向其中添加null,因為null表示任何物件.
List<? super Apple> list2能夠向其中添加Apple,HFSApple等Apple及其子類,但是不能添加Apple的父類,在獲取list2中的物件時只能是Object,這是為什么呢?原來? super Apple表示Apple的或者Apple的父類,有一個有限長度的型別范圍,直到Object,既然有范圍,就可以在這個范圍之上向list2添加Apple或者Apple的子類,但是在獲取的時候,因為時一個有限的范圍,list2不知道應該回傳具體持有的是什么型別,所以只能回傳所有型別的父類Objct.
list3 簡單表述了? super Apple的一個應用場景,
那么我們做出如下總結:
-
extends 可用于的回傳型別限定,不能用于引數型別限定,
-
super 可用于引數型別限定,不能用于回傳型別限定,
泛型的擦除和限制


List<Integer> list0 = new ArrayList<>(); List<String> list1 = new ArrayList<>(); System.out.println(list0.getClass()==list1.getClass()); 運行結果:true
泛型不能使用instanceof,因為型別擦除,但是可用動態的呼叫isInstance()
泛型中不允許使用new T()這樣創建新的物件,但是我們可用使用其他方法創建,第一種時使用newInstance(),這種方法必須保證有默認的建構式;另一種是構造一個工廠物件,由工廠生成物件.
注意
//在泛型方法中添加上下邊界限制的時候,必須在權限宣告與回傳值之間的<T>上添加上下邊界,即在泛型宣告的時候添加
//public <T> T showKeyName(Generic<T extends Number> container),編譯器會報錯:"Unexpected bound"
public <T extends Number> T showKeyName(Generic<T> container){
System.out.println("container key :" + container.getKey());
T test = container.getKey();
return test;
}
下面的這個例子是不可以的:
List<String>[] ls = new ArrayList<String>[10];
而使用通配符創建泛型陣列是可以的,如下面這個例子:
List<?>[] ls = new ArrayList<?>[10];
這樣也是可以的:
List<String>[] ls = new ArrayList[10];
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/250034.html
標籤:Java
上一篇:snmp4j發送v3trap
下一篇:Java泛型——泛型矩陣類
