国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

java范型

2019-11-06 06:26:38
字體:
來源:轉載
供稿:網友

這里寫圖片描述

java泛型由來的動機

理解Java泛型最簡單的方法是把它看成一種便捷語法,能節省你某些Java類型轉換(casting)上的操作;以及防止代碼運行時出現ClassCastException。

泛型的構成

范型類、范型接口public interface List<T> extends Collection<T> { //get方法實際返回的是一個類型為T的對象,T是在List<T>聲明中的類型變量。 T get(int index); <T1> void put(T1 t); Iterator<E> iterator();...}泛型方法和構造器(Constructor)//如果方法和構造器上聲明了一個或多個類型變量,它們也可以泛型化//使用場景:靜態、final、工具類public final class Collections2 { public static <E> Collection<E> filter(Collection<E> unfiltered, PRedicate<? super E> predicate) { ... }}

遍歷使用

類庫中的很多類,諸如Iterator,功能都有所增強,被泛型化。List接口里的iterator()方法現在返回的是Iterator,由它的T next()方法返回的對象不需要再進行類型轉換,你直接得到正確的類型。使用foreach,“for each”語法同樣受益于泛型。for (Iterator<String> iter = str.iterator(); iter.hasNext();) {String s = iter.next();System.out.print(s);}for (String s: str) {System.out.print(s);}

自動封裝(Autoboxing)和自動拆封(Autounboxing)

在使用Java泛型時,autoboxing/autounboxing這兩個特征會被自動的用到,就像下面的這段代碼:List<Integer> ints = new ArrayList<Integer>();ints.add(0);ints.add(1);int sum = 0;for (int i : ints) {sum += i;}要明白的一點是,封裝和解封會帶來性能上的損失

通配符

“Producer Extends” – 如果你需要一個只讀List,用它來produce T,那么使用? extends T。“Consumer Super” – 如果你需要一個只寫List,用它來consume T,那么使用? super T。

如果需要同時讀取以及寫入,那么我們就不能使用通配符了。

如何閱讀過一些Java集合類的源碼,可以發現通常我們會將兩者結合起來一起用,比如像下面這樣:

public class Collections { public static <T> void copy(List<? super T> dest, List<? extends T> src) { for (int i=0; i<src.size(); i++) dest.set(i, src.get(i)); }}

類型擦除

  - Java泛型中最令人苦惱的地方或許就是類型擦除了,特別是對于有C++經驗的程序員。類型擦除就是說Java泛型只能用于在編譯期間的靜態類型檢查,然后編譯器生成的代碼會擦除相應的類型信息,這樣到了運行期間實際上JVM根本就知道泛型所代表的具體類型。這樣做的目的是因為Java泛型是1.5之后才被引入的,為了保持向下的兼容性,所以只能做類型擦除來兼容以前的非泛型代碼。對于這一點,如果閱讀Java集合框架的源碼,可以發現有些類其實并不支持泛型。

public class Generic { static List<Apple> apples = Arrays.asList(new Apple()); static List<Fruit> fruit = Arrays.asList(new Fruit()); static class Reader<T> { T readExact(List<T> list) { return list.get(0); } } static void f1() { Reader<Fruit> fruitReader = new Reader<Fruit>(); // Errors: List<Fruit> cannot be applied to List<Apple>. // Fruit f = fruitReader.readExact(apples); } //但是按照我們通常的思維習慣,Apple和Fruit之間肯定是存在聯系,然而編譯器卻無法識別, // 那怎么在泛型代碼中解決這個問題呢?我們可以通過使用通配符來解決這個問題: static class CovariantReader<T> { T readCovariant(List<? extends T> list) { return list.get(0); } } static void f2() { CovariantReader<Fruit> fruitReader = new CovariantReader<Fruit>(); Fruit f = fruitReader.readCovariant(fruit); Fruit a = fruitReader.readCovariant(apples); } static void add() { // Wildcards allow covariance: List<? extends Fruit> flist = new ArrayList<Apple>(); // Compile Error: can't add any type of object: // flist.add(new Apple()) // flist.add(new Orange()) // flist.add(new Fruit()) // flist.add(new Object()) flist.add(null); // Legal but uninteresting // We Know that it returns at least Fruit: Fruit f = flist.get(0); //答案是否定,Java編譯器不允許我們這樣做,為什么呢? // 對于這個問題我們不妨從編譯器的角度去考慮。因為List<? extends Fruit> flist它自身可以有多種含義:// List<? extends Fruit> flist = new ArrayList<Fruit>();// List<? extends Fruit> flist = new ArrayList<Apple>();// List<? extends Fruit> flist = new ArrayList<Orange>(); } public static void main(String[] args) { f1(); f2(); } static class Fruit { } static class Apple extends Fruit { } static class Orange extends Fruit { }}4種案例public class Test2 { public static void quetion1() { //問題一:在Java中不允許創建泛型數組,類似下面這樣的做法編譯器會報錯: List<Integer>[] arrayOfLists = new List<Integer>[2]; // compile-time error //對于下面這段代碼還是很好理解,字符串數組不能存放整型元素, //而且這樣的錯誤往往要等到代碼運行的時候才能發現,編譯器是無法識別的。 Object[] strings = new String[2]; strings[0] = "hi"; // OK strings[1] = 100; // An ArrayStoreException is thrown. //由于運行時期類型信息已經被擦除,JVM實際上根本就不知道new ArrayList<String>()和new ArrayList<Integer>()的區別 Class c1 = new ArrayList<String>().getClass(); Class c2 = new ArrayList<Integer>().getClass(); System.out.println(c1.getName() + c2.getName() + (c1 == c2)); // true } //問題二(還沒理解,參看尾部的參考):繼續復用我們上面的Node的類,對于泛型代碼,Java編譯器實際上還會偷偷幫我們實現一個Bridge method。? public static void question2() { //最佳實踐,建設類型推斷,顯示設置返回類型。 } // 問題三:正如我們上面提到的,Java泛型很大程度上只能提供靜態類型檢查, // 然后類型的信息就會被擦除,所以像下面這樣利用類型參數創建實例的做法編譯器不會通過: public static <E> void append(List<E> list) { E elem = new E(); // compile-time error list.add(elem); } //但是如果某些場景我們想要需要利用類型參數創建實例,我們應該怎么做呢?可以利用反射解決這個問題: public static <E> void append(List<E> list, Class<E> cls) throws Exception { E elem = cls.newInstance(); // OK list.add(elem);// List<String> ls = new ArrayList();// append(ls, String.class); } //問題四:我們無法對泛型代碼直接使用instanceof關鍵字, //因為Java編譯器在生成代碼的時候會擦除所有相關泛型的類型信息, //正如我們上面驗證過的JVM在運行時期無法識別出ArrayList<Integer>和ArrayList<String>的之間的區別: public static <E> void rtt(List<E> list) { if (list instanceof ArrayList<Integer>) { // compile-time error // ... } } //和上面一樣,我們可以使用通配符重新設置bounds來解決這個問題: public static void rtti(List<?> list) { if (list instanceof ArrayList<?>) { // OK; instanceof requires a reifiable type // ... } } public class Node<T extends Comparable<T>> { private T data; private Node<T> next; public Node(T data, Node<T> next) { this.data = data; this.next = next; } public T getData() { return data; } // ... }}

Effective Java 范型

不要在新代碼中使用原生態類型:Set< Object >是個參數化類型,可以包含任何對象類型的一個集合;Set< ?>則是一個通配符類型,表示可以包含某種未知對象類型的一個集合;Set 則是一個原生態類型,它脫離類范型系統。前兩者是安全的,后一種不安全。

消除 unchecked cast warnings;unchecked conversion warnings.

列表優先于數組:數組提供了運行時的類型安全,但是沒有編譯時的類型安全。

Java 泛型詳解


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 甘南县| 河南省| 和平区| 临沭县| 黑山县| 昆山市| 庄浪县| 文化| 准格尔旗| 尉犁县| 大悟县| 嵊泗县| 鄂伦春自治旗| 冀州市| 抚州市| 龙陵县| 阳曲县| 天台县| 肇庆市| 台中市| 屏南县| 大方县| 班戈县| 静乐县| 郑州市| 葫芦岛市| 蓬溪县| 阆中市| 松溪县| 黄山市| 吴忠市| 博乐市| 昆明市| 壶关县| 萨迦县| 尚义县| 明光市| 杭锦后旗| 色达县| 开江县| 南平市|