Java™ 教程(泛型通配符捕獲和Helper方法)

泛型通配符捕獲和Helper方法

在某些狀況下,編譯器會推斷出通配符的類型,例如,列表能夠定義爲List<?>,可是在評估表達式時,編譯器會從代碼中推斷出特定類型,此場景稱爲通配符捕獲。html

在大多數狀況下,你沒必要擔憂通配符捕獲,除非你看到包含短語「capture of」的錯誤消息。java

WildcardError示例在編譯時產生捕獲錯誤:segmentfault

import java.util.List;

public class WildcardError {

    void foo(List<?> i) {
        i.set(0, i.get(0));
    }
}

在此示例中,編譯器將i輸入參數處理爲Object類型,當foo方法調用List.set(int, E)時,編譯器沒法確認插入到列表中的對象的類型,而且會產生錯誤,發生此類錯誤時,一般意味着編譯器認爲你爲變量分配了錯誤的類型,出於這個緣由,泛型被添加到Java語言中 — 在編譯時強制執行類型安全。api

由Oracle的JDK 7 javac實現編譯時,WildcardError示例生成如下錯誤:安全

WildcardError.java:6: error: method set in interface List<E> cannot be applied to given types;
    i.set(0, i.get(0));
     ^
  required: int,CAP#1
  found: int,Object
  reason: actual argument Object cannot be converted to CAP#1 by method invocation conversion
  where E is a type-variable:
    E extends Object declared in interface List
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Object from capture of ?
1 error

在此示例中,代碼嘗試執行安全操做,那麼如何解決編譯器錯誤?你能夠經過編寫捕獲通配符的私有Helper方法來修復它,在這種狀況下,你能夠經過建立私有Helper方法fooHelper來解決此問題,如WildcardFixed中所示:oracle

public class WildcardFixed {

    void foo(List<?> i) {
        fooHelper(i);
    }


    // Helper method created so that the wildcard can be captured
    // through type inference.
    private <T> void fooHelper(List<T> l) {
        l.set(0, l.get(0));
    }

}

因爲Helper方法,編譯器使用推斷來肯定T是調用中的CAP#1(捕獲變量),該示例如今成功編譯。app

按照慣例,Helper方法一般命名爲originalMethodNameHelperui

如今考慮一個更復雜的例子,WildcardErrorBadcode

import java.util.List;

public class WildcardErrorBad {

    void swapFirst(List<? extends Number> l1, List<? extends Number> l2) {
      Number temp = l1.get(0);
      l1.set(0, l2.get(0)); // expected a CAP#1 extends Number,
                            // got a CAP#2 extends Number;
                            // same bound, but different types
      l2.set(0, temp);        // expected a CAP#1 extends Number,
                            // got a Number
    }
}

在這個例子中,代碼正在嘗試不安全的操做,例如,考慮如下對swapFirst方法的調用:htm

List<Integer> li = Arrays.asList(1, 2, 3);
List<Double>  ld = Arrays.asList(10.10, 20.20, 30.30);
swapFirst(li, ld);

List<Integer>List<Double>都符合List<? extends Number>的標準,從Integer值列表中獲取項目並嘗試將其放入Double值列表中顯然是不正確的。

使用Oracle的JDK javac編譯器編譯代碼會產生如下錯誤:

WildcardErrorBad.java:7: error: method set in interface List<E> cannot be applied to given types;
      l1.set(0, l2.get(0)); // expected a CAP#1 extends Number,
        ^
  required: int,CAP#1
  found: int,Number
  reason: actual argument Number cannot be converted to CAP#1 by method invocation conversion
  where E is a type-variable:
    E extends Object declared in interface List
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Number from capture of ? extends Number
WildcardErrorBad.java:10: error: method set in interface List<E> cannot be applied to given types;
      l2.set(0, temp);      // expected a CAP#1 extends Number,
        ^
  required: int,CAP#1
  found: int,Number
  reason: actual argument Number cannot be converted to CAP#1 by method invocation conversion
  where E is a type-variable:
    E extends Object declared in interface List
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Number from capture of ? extends Number
WildcardErrorBad.java:15: error: method set in interface List<E> cannot be applied to given types;
        i.set(0, i.get(0));
         ^
  required: int,CAP#1
  found: int,Object
  reason: actual argument Object cannot be converted to CAP#1 by method invocation conversion
  where E is a type-variable:
    E extends Object declared in interface List
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Object from capture of ?
3 errors

這裏沒有Helper方法來解決這個問題,由於代碼根本就是錯誤的。


上一篇:泛型通配符

相關文章
相關標籤/搜索