- 高級語法:通配符
- 最佳實踐
本文介紹泛型的高級語法已經最佳實踐。Java 泛型的很多限制都可以從上篇中的原理(Java 泛型機制) 來解釋。
通配符通配符是使用的角度而言的,在編譯器期間執行更加嚴格的檢查。
子類限定通配符語法:Pair<? extends Employee>
繼承關系:限制:只需要從編譯器如何翻譯代碼塊的角度思考這些限制。
Employee emp = new Employee();Manager ceo = new Manager();Manager cfo = new Manager();Pair<Manager> managerBuddies = new Pair<Manager>(ceo, cfo);Pair<? extends Employee> wildcardBuddies = managerBuddies; // OK// 編譯器只知道是某個Employee的子類,編譯器推斷不出具體的類型// 限制: 編譯器拒絕任何特定類型// void setFirst(? extends Employee)wildcardBuddies.setFirst(ceo); // compile-time errorwildcardBuddies.setFirst(emp); // compile-time errorwildcardBuddies.setFirst(new Object()); // compile-time error// ? extends Employee getFirst()Employee ret = wildcardBuddies.getFirst(); // OK
超類限定通配符語法:Pair<? super Manager>
繼承關系:
限制:
Employee e1 = new Employee();Employee e2 = new Employee();Manager ceo = new Manager();Pair<Employee> employeeBuddies = new Pair<Employee>();Pair<? super Manager> wildcardBuddies = employeeBuddies; // OK// 編譯器值知道是某個Manager的父類// 限制: 編譯器拒絕任何特定類型// ? super ManagerManager ret = wildcardBuddies.getFirst(); // compile-time error// void setFirst(? super Manager)wildcardBuddies.setFirst(e1); // compile-time errorwildcardBuddies.setFirst(ceo); // OK
更加嚴格的寫法:
public static <T extends Comparable> T min(T[] a);// 更加嚴格的寫法public static <T extends Comparable<? super T>> T min(T[] a);
解釋:該泛型方法使用Comparable<? super T>
進行擦除,現在 compareTo 寫成:int compareTo(? super T). 表明只需要 T的父類有 compareTo() 方法即可.例如:GregorianCalendar 是 Calendar 的子類,Calendar實現了Comparable
語法:Pair<?>
限制:
? getFirst(); // 只能賦給一個Objectvoid setFirst(?); // 無法調用, Object也不行
存在的理由:對于簡單的操作,代替泛型方法,更加具有可讀性。比如:
public static <T> boolean hasNulls(Pair<T> p)// 可以寫成:public static boolean hasNull(Pair<?> p) { return p.getFirst() == null || p.getSecond() == null; }
局限:? 不能作為一種類型。? t = xx 非法。這樣導致只能代替操作簡單的泛型方法。比如swap都寫不了.
public static void swap(Pair<?> p) { ? t = p.getFirst; // ERROR p.setFirst(p.getSecond); p.setSecond(t);}// 這時候只能:public static <T> void swapHelper(Pair<T> p) { T t = p.getFirst; // ERROR p.setFirst(p.getSecond); p.setSecond(t);}public static void swap(Pair<?> p) { swapHelper(p);}
if (a instanceof Pair<String>) // same as "a instanceof Pair"if (a instanceof Pair<T>) // T is ignoredPair<String> p = (Pair<String>)a; // can only test that a is a PairPair<String> stringPair = ...;Pair<Employee> employeePair = ...;if (stringPair.getClass() == employeePair.getClass()) // they are equal, Pair.class
3.泛型類無法擴展 Throwable,也無法拋出泛型類.// 1. 泛型類無法擴展 Throwablepublic class PRoblem<T> extends Exception {/*...*/} // ERROR -- can't extend Throwable(編譯無法通過)// 2. 不能拋出泛型類示例public static <T extends Throwable> void doWork(Class<T> t) { try { do work } catch (T e) {// ERROR -- cant't catch type variable Logger.global.info(...) }}// 正確時間:public static <T extends Throwable> void doWork(Class<T> t) { try { do work } catch (Throwable realCause) { t.intCause(realCause); throw t; }}
4.參數化類型的數組不合法,而是采用容器類存儲.Pair<String>[] table = new Pair<String>[10]; // ERROR
5.可以聲明類型參數,但不能實例化類型參數new T(...), new T[...], T.class 非法. 但是可以聲明.
問題1.
public Pair() { first = new T(); second = new T(); } // ERRORfirst = T.class.newInstance(); // ERROR// 最佳實踐: 指定T的類型public static <T> Pair<T> makePair(Class<T> clazz) { try { return new Pair(clazz.newInstance(), clazz.newInstance()); } catch (Exception e) { return null; }}Pair<String> stringPair = Pair.<String>makePair(String.class);// OK
問題2.
public static <T extends Comparable<? super T>> T[] minmax(T... a) { // T[] mm = new T[2]; // ERROR, Type parameter 'T' cannot be instantiated directly Object[] mm = new Object[2]; mm[0] = a[0]; mm[1] = a[1]; return (T[])mm; // T 被擦成Comparable, 運行時會ClassCastException, 而Object不是Comparable}public static void main(String[] args) { String[] ss = minmax("Tom", "Dick", "Harry"); // ClassCastException!!!}// Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Comparable}// 最佳實踐: public static <T extends Comparable<? super T>> T[] minmax(T... a) { T[] mm = (T[])Array.newInstance(a.getClass().getComponentType(), 2); ... return mm;}
問題3.
public class ArrayList<T> { private T[] elements; public ArrayList() { elements = (T[]) new Object[10]; } public T get(int n) { return elements[n]; } public void set(int n, T e) { elements[n] = e; } public static void main(String[] args) { ArrayList<String> src = new ArrayList<String>(); src.set(0, "hello"); src.set(1, "world"); // String[]和Object[]是不同的類型.// String[] copy = src.toArray(); // Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String; String[] copy2 = new String[100]; src.toArray(copy2); System.out.println(Arrays.toString(copy2)); // OK } public T[] toArray() { // 可以分析:elements.getClass().getComponentType()實質是Object, 所以返回的類型實質是:Object[], T[] result = (T[]) Array.newInstance(elements.getClass().getComponentType(), elements.length); return result; } public T[] toArray(T[] src) { for (int i = 0; i < elements.length; ++i) { src[i] = elements[i]; } return src; }}
6.不能在靜態域或者方法中使用類型變量. 因為靜態域是類共享的.7. 當T和S有繼承關系時,Pair<T>
和 Pair<S>
沒有繼承關系.8. Java SE5.0 增加了對泛型的反射API.Java SE5.0 增加了 java.lang.reflect.Type 來支持類型的反射信息.public void
新聞熱點
疑難解答