[英]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));
}
這本書非常混亂。 它假定<?>
以某種方式解決了無法實例化具有未知T
的List
的問題。 恕我直言,這是垃圾。 必須指定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.