Android为TV端助力 转载:Java 泛型
一. 泛型概念的提出(為什么需要泛型)?
首先,我們看下下面這段簡短的代碼:
1 public class GenericTest {2 3 public static void main(String[] args) {4 List list = new ArrayList();5 list.add("qqyumidi");6 list.add("corn");7 list.add(100);8 9 for (int i = 0; i < list.size(); i++) { 10 String name = (String) list.get(i); // 1 11 System.out.println("name:" + name); 12 } 13 } 14 }定義了一個List類型的集合,先向其中加入了兩個字符串類型的值,隨后加入一個Integer類型的值。這是完全允許的,因為此時list默認的類型為Object類型。在之后的循環(huán)中,由于忘記了之前在list中也加入了Integer類型的值或其他編碼原因,很容易出現(xiàn)類似于//1中的錯誤。因為編譯階段正常,而運行時會出現(xiàn)“java.lang.ClassCastException”異常。因此,導(dǎo)致此類錯誤編碼過程中不易發(fā)現(xiàn)。
?在如上的編碼過程中,我們發(fā)現(xiàn)主要存在兩個問題:
1.當(dāng)我們將一個對象放入集合中,集合不會記住此對象的類型,當(dāng)再次從集合中取出此對象時,改對象的編譯類型變成了Object類型,但其運行時類型任然為其本身類型。
2.因此,//1處取出集合元素時需要人為的強制類型轉(zhuǎn)化到具體的目標類型,且很容易出現(xiàn)“java.lang.ClassCastException”異常。
那么有沒有什么辦法可以使集合能夠記住集合內(nèi)元素各類型,且能夠達到只要編譯時不出現(xiàn)問題,運行時就不會出現(xiàn)“java.lang.ClassCastException”異常呢?答案就是使用泛型。
?
二.什么是泛型?
泛型,即“參數(shù)化類型”。一提到參數(shù),最熟悉的就是定義方法時有形參,然后調(diào)用此方法時傳遞實參。那么參數(shù)化類型怎么理解呢?顧名思義,就是將類型由原來的具體的類型參數(shù)化,類似于方法中的變量參數(shù),此時類型也定義成參數(shù)形式(可以稱之為類型形參),然后在使用/調(diào)用時傳入具體的類型(類型實參)。
?看著好像有點復(fù)雜,首先我們看下上面那個例子采用泛型的寫法。
1 public class GenericTest {2 3 public static void main(String[] args) {4 /*5 List list = new ArrayList();6 list.add("qqyumidi");7 list.add("corn");8 list.add(100);9 */ 10 11 List<String> list = new ArrayList<String>(); 12 list.add("qqyumidi"); 13 list.add("corn"); 14 //list.add(100); // 1 提示編譯錯誤 15 16 for (int i = 0; i < list.size(); i++) { 17 String name = list.get(i); // 2 18 System.out.println("name:" + name); 19 } 20 } 21 }采用泛型寫法后,在//1處想加入一個Integer類型的對象時會出現(xiàn)編譯錯誤,通過List<String>,直接限定了list集合中只能含有String類型的元素,從而在//2處無須進行強制類型轉(zhuǎn)換,因為此時,集合能夠記住元素的類型信息,編譯器已經(jīng)能夠確認它是String類型了。
結(jié)合上面的泛型定義,我們知道在List<String>中,String是類型實參,也就是說,相應(yīng)的List接口中肯定含有類型形參。且get()方法的返回結(jié)果也直接是此形參類型(也就是對應(yīng)的傳入的類型實參)。下面就來看看List接口的的具體定義:
1 public interface List<E> extends Collection<E> {2 3 int size();4 5 boolean isEmpty();6 7 boolean contains(Object o);8 9 Iterator<E> iterator(); 10 11 Object[] toArray(); 12 13 <T> T[] toArray(T[] a); 14 15 boolean add(E e); 16 17 boolean remove(Object o); 18 19 boolean containsAll(Collection<?> c); 20 21 boolean addAll(Collection<? extends E> c); 22 23 boolean addAll(int index, Collection<? extends E> c); 24 25 boolean removeAll(Collection<?> c); 26 27 boolean retainAll(Collection<?> c); 28 29 void clear(); 30 31 boolean equals(Object o); 32 33 int hashCode(); 34 35 E get(int index); 36 37 E set(int index, E element); 38 39 void add(int index, E element); 40 41 E remove(int index); 42 43 int indexOf(Object o); 44 45 int lastIndexOf(Object o); 46 47 ListIterator<E> listIterator(); 48 49 ListIterator<E> listIterator(int index); 50 51 List<E> subList(int fromIndex, int toIndex); 52 }我們可以看到,在List接口中采用泛型化定義之后,<E>中的E表示類型形參,可以接收具體的類型實參,并且此接口定義中,凡是出現(xiàn)E的地方均表示相同的接受自外部的類型實參。
自然的,ArrayList作為List接口的實現(xiàn)類,其定義形式是:
1 public class ArrayList<E> extends AbstractList<E> 2 implements List<E>, RandomAccess, Cloneable, java.io.Serializable {3 4 public boolean add(E e) {5 ensureCapacityInternal(size + 1); // Increments modCount!!6 elementData[size++] = e;7 return true;8 }9 10 public E get(int index) { 11 rangeCheck(index); 12 checkForComodification(); 13 return ArrayList.this.elementData(offset + index); 14 } 15 16 //...省略掉其他具體的定義過程 17 18 }由此,我們從源代碼角度明白了為什么//1處加入Integer類型對象編譯錯誤,且//2處get()到的類型直接就是String類型了。
?
三.自定義泛型接口、泛型類和泛型方法
從上面的內(nèi)容中,大家已經(jīng)明白了泛型的具體運作過程。也知道了接口、類和方法也都可以使用泛型去定義,以及相應(yīng)的使用。是的,在具體使用時,可以分為泛型接口、泛型類和泛型方法。
自定義泛型接口、泛型類和泛型方法與上述Java源碼中的List、ArrayList類似。如下,我們看一個最簡單的泛型類和方法定義:
1 public class GenericTest {2 3 public static void main(String[] args) {4 5 Box<String> name = new Box<String>("corn");6 System.out.println("name:" + name.getData());7 }8 9 } 10 11 class Box<T> { 12 13 private T data; 14 15 public Box() { 16 17 } 18 19 public Box(T data) { 20 this.data = data; 21 } 22 23 public T getData() { 24 return data; 25 } 26 27 }?在泛型接口、泛型類和泛型方法的定義過程中,我們常見的如T、E、K、V等形式的參數(shù)常用于表示泛型形參,由于接收來自外部使用時候傳入的類型實參。那么對于不同傳入的類型實參,生成的相應(yīng)對象實例的類型是不是一樣的呢?
1 public class GenericTest {2 3 public static void main(String[] args) {4 5 Box<String> name = new Box<String>("corn");6 Box<Integer> age = new Box<Integer>(712);7 8 System.out.println("name class:" + name.getClass()); // com.qqyumidi.Box9 System.out.println("age class:" + age.getClass()); // com.qqyumidi.Box 10 System.out.println(name.getClass() == age.getClass()); // true 11 12 } 13 14 }由此,我們發(fā)現(xiàn),在使用泛型類時,雖然傳入了不同的泛型實參,但并沒有真正意義上生成不同的類型,傳入不同泛型實參的泛型類在內(nèi)存上只有一個,即還是原來的最基本的類型(本實例中為Box),當(dāng)然,在邏輯上我們可以理解成多個不同的泛型類型。
究其原因,在于Java中的泛型這一概念提出的目的,導(dǎo)致其只是作用于代碼編譯階段,在編譯過程中,對于正確檢驗泛型結(jié)果后,會將泛型的相關(guān)信息擦出,也就是說,成功編譯過后的class文件中是不包含任何泛型信息的。泛型信息不會進入到運行時階段。
對此總結(jié)成一句話:泛型類型在邏輯上看以看成是多個不同的類型,實際上都是相同的基本類型。
轉(zhuǎn)載于:https://www.cnblogs.com/xiaoxiaing/p/6425967.html
總結(jié)
以上是生活随笔為你收集整理的Android为TV端助力 转载:Java 泛型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 清缅战争死亡人数
- 下一篇: 点击双人同行游戏出现failedtoin