簡體   English   中英

實例化泛型類型ArrayList <T>

[英]Instantiating generic type ArrayList<T>

我是泛型的新手並在文章中讀到“參數化類型,例如ArrayList<T> ,不可實例化 - 我們無法創建它們的實例”。

完全引用,來自Java in a Nutshell:

參數化類型(如ArrayList<T> )不可實例化 - 我們無法創建它們的實例。 這是因為<T>只是一個類型參數 - 僅僅是真正類型的占位符。 只有當我們為type參數(例如, ArrayList<String> )提供具體值時,才會完全形成類型,並且我們可以創建該類型的對象。

如果我們想要使用的類型在編譯時未知,則會出現問題。 幸運的是,Java類型系統能夠適應這一概念。 它通過具有未知類型的明確概念來表達,其表示為<?>

我知道它不應該是可實例化的,因為具體(實際)類型是未知的。 如果是這樣,為什么下面的代碼編譯沒有錯誤?

public class SampleTest {

    public static <T> List<T> getList(T... elements) {

        List<T> lst = new ArrayList<>(); // shouldn't this line return an error? 

        return lst;
    }
}

我知道我對仿制葯的理解存在差距。 有人能指出我在這里缺少什么嗎?

因為T是另一個泛型類型參數。

泛型的全部目的是使類型可參數化。 因此調用者可以指定類型。 這可以在多個層中完成:調用者也可以是通用的,並讓其調用者指定類型。

public static void main(String[] args)
{
  foo(7);
}

public static <T> void foo(T value)
{
  bar(value);
}

public static <U> void bar(U value)
{
  baz(value);
}

public static <V> void baz(V value)
{
  System.out.println(value.getClass().getSimpleName());
}

打印出來

Integer

參數化類型(例如ArrayList<T> )不可實例化

意思是:你不能創建未知T的ArrayList。它必須在編譯時指定。 但它可以通過另一種通用方法間接完成。 在你的情況下,它是另一個 T ,它將由你的通用getList的調用者再次指定。


通配符<?>是不同的東西。 它用於指定兼容性。 <?>是避免指定類型的語法。 您可以使用extends來要求基本類型或接口。 但是,您無法使用通配符創建實例。

  List<?> list = new ArrayList<String>();
  list = new ArrayList<Integer>();

否則這是不可能的。 在參數規范中使用它時最有意義,例如:

  public static int foo(List<? extends Comparable> list)
  {
     return list.get(1).compareTo(list.get(2));
  }

這本書非常混亂。 它假定<?>以某種方式解決了無法實例化具有未知TList的問題。 恕我直言,這是垃圾。 必須指定T才能創建實例。

你提到的代碼可以編譯,因為在調用方法之前,實際上沒有初始化Object“lst”。 由於該方法知道它將獲得類型為T的var-args參數,因此可以在此方案中進行編譯。 以下面的Wrapper類為例:

public class Wrapper<T> {


    public static <T> List<T> getList(T... elements){
        List<T> lst = new ArrayList<>();
        for(T element: elements) {
            lst.add(element);
        }
        return lst;
}

}

此代碼可以編譯,因為尚未調用該方法。 調用該方法時,Type T將是我們作為var-args參數傳遞的類型,代碼將沒有問題編譯。 讓我們在主要方法中測試一下:

 public static void main( String[] args ){

      System.out.println(Wrapper.getList("Hi", "Hello", "Yo"));

 }

輸出是:

[Hi, Hello, Yo]

但是,讓我們生成編譯時錯誤,以查看文章在我們的main方法中討論的內容:

Wrapper<T> myWrap = new Wrapper<>();

我們實際上是在上面的代碼中嘗試初始化Wrapper類的通用Object,但是未知。 由於占位符的值即使在調用方法時也是未知的,因此會導致編譯時錯誤,而在getList方法中創建類型為T的List不會導致編譯時錯誤,因為它將初始化為調用方法時的類型。

一旦調用方法 - >您正在使用具體值。

該方法定義了T,稍后您將在返回類型和參數列表中使用它。

public static <T> List<T> getList(T... elements)

一旦你將從特定類型發送第一個參數 - >合同將強制你下一個參數。

List<? extends Object> list = getList("", 1); - >在這種情況下,java在字符串和整數之間找不到共同點,所以它使用最基本的連接“對象”

List<String> list2 = getList("test", "test2"); - >在這里你可以看到,因為所有的參數都是字符串 - java發現它的共同點並用它作為T.

書中的具體段落沒有任何意義,也是錯誤的。 new ArrayList<T>()完全沒問題,前提是我們在一個名為T的類型參數的范圍內(我們所在的泛型類的類型參數,或者我們所在的泛型方法的類型參數)。

new ArrayList<T>()可以實例化,而不是new ArrayList<String>() - 兩者都編譯為相同的字節碼,並且只是在運行時實例化一個ArrayList對象。 該對象在運行時對其類型參數一無所知,因此在運行時不需要知道T來實例化它。 參數化類型( new ArrayList<T> )的實例化表達式中的type參數只是由編譯器用於對傳遞給構造函數的參數進行類型檢查(在這種情況下沒有)並找出返回的類型表達方式; 它不以任何其他方式使用。

順便說一下,該方法不需要接收任何類型為T參數,也不需要接收包含T的任何類型的參數,以使其工作。 接收任何參數的方法仍然可以實例化並返回ArrayList<T>完全正常:

public static <T> List<T> emptyList() {
    List<T> lst = new ArrayList<T>();
    return lst;
}

此外,書中出現此語句的部分與實例化沒有任何關系 - 該部分是關於通配符的,並且通配符實際上與對象實例化沒有任何關系。 所以我不確定為什么他們(錯誤地)在那里提到它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM