2009-08-06 65 views
8
public void wahey(List<Object> list) {} 

wahey(new LinkedList<Number>()); 

對方法的調用不會進行類型檢查。我甚至不能投的參數如下:爲什麼列表<Number>不是列表<Object>的子類型?

wahey((List<Object>) new LinkedList<Number>()); 

從我的研究,我已經收集,對於不允許這樣做的原因是類型安全。如果我們允許做上述情況,那麼我們可以有以下幾種:

List<Double> ld; 
wahey(ld); 

裏面的方法wahey,我們可以一些字符串添加到輸入表(作爲參數保持List<Object>參考)。現在,在方法調用之後,ld引用一個類型爲List<Double>的列表,但實際列表包含一些String對象!

這似乎不同於普通的Java沒有泛型的工作方式。例如:

Object o; 
Double d; 
String s; 

o = s; 
d = (Double) o; 

我們在這裏做的基本上是同樣的事情,但這次將通過編譯時檢查,只有在運行時出現故障。帶有列表的版本不會編譯。

這使我相信這純粹是一個關於泛型類型限制的設計決定。我希望對這個決定有所評論?

+1

相關(不完全重複,雖然正確答案基本相同):http:// stackoverflow。com/questions/251298/why-is-someclass-super-t-not-equivalent-to-someclasst-in-java-generic-types – Kip 2009-08-06 17:44:04

+3

藝術術語是**協變** - 我提到這是因爲它使它更容易查找它,包括堆棧溢出(如果你想看看我要說的 - 在C#的上下文中,但它也適用於Java - 搜索[協方差用戶: 95810])。 – 2009-08-06 17:44:25

+0

''wahey((列表)(列表)新的ArrayList ())''將會被編譯,儘管這不被推薦。 – Robin479 2017-04-04 06:58:37

回答

9

你在「沒有泛型」的例子中做了什麼是一個演員,這表明你正在做一些類型不安全的事情。泛型等效是:

Object o; 
List<Double> d; 
String s; 

o = s; 
d.add((Double) o); 

其行爲以同樣的方式(編譯,但在運行時出現故障)。不允許你詢問的行爲的原因是因爲它允許隱含類型不安全的操作,這在代碼中很難注意到。例如:

public void Foo(List<Object> list, Object obj) { 
    list.add(obj); 
} 

這看起來完全正常和類型安全的,直到你這樣稱呼它:

List<Double> list_d; 
String s; 

Foo(list_d, s); 

這看起來也類型安全的,因爲你作爲主叫方不一定知道是什麼Foo將處理其參數。

所以在這種情況下,你有兩個看起來類型安全的代碼位,它們一起最終成爲類型不安全的。這很糟糕,因爲它是隱藏的,因此難以避免並且難以調試。

+0

啊,我明白了。非常感謝你。 :) – 2009-08-06 17:47:02

8

考慮,如果它是...

List<Integer> nums = new ArrayList<Integer>(); 
List<Object> objs = nums 
objs.add("Oh no!"); 
int x = nums.get(0); //throws ClassCastException 

你就可以在父類的東西添加到列表中,這可能不是什麼它以前宣佈的,它上面的例子演示,導致各種問題。因此,這是不允許的。

+0

這是正確的,但問題表明他已經理解了這一點,並且想知道爲什麼決定禁止此行爲,同時仍允許其他形式的類型不安全行爲。 – 2009-08-06 17:41:45

+0

我已經理解了這個例子。我試圖在問題中解釋這種情況(如泰勒所說),但是感謝您的評論。 – 2009-08-06 17:48:03

+1

這種類型的不安全行爲實際上*是*數組允許的。以下編譯很好,但會產生一個運行時錯誤:'Object [] arr = new Integer [2]; arr [0] =「哦不!」;' – Kip 2009-08-06 17:48:36

4

由於仿製藥是如何工作的,它們不是彼此的亞型。你想要聲明你的功能是這樣的:

public void wahey(List<?> list) {} 

然後它會接受任何擴展對象的列表。你也可以這樣做:

public void wahey(List<? extends Number> list) {} 

這樣可以讓你獲得一些Number的子類的列表。

我建議您拿起Maurice Naftalin的「Java Generics and Collections」副本& Philip Wadler。

+0

+1,這是泛型的工作原理。 ** DO **提取Java泛型和集合的副本。真棒書! – WolfmanDragon 2009-08-06 17:49:17

+0

這不是我的問題。我知道這些是泛型分型的規則。 – 2009-08-06 17:51:34

3

這裏基本上有兩個抽象維度,即列表抽象和內容抽象。在列表抽象中改變是完全正確的 - 例如說,它是一個LinkedList或一個ArrayList - 但是它不是進一步限制內容的好方法,說:這個(包含對象的列表)是一個(鏈接列表,它是隻有數字)。因爲任何知道它爲(保存對象的列表)的引用通過它的類型的合約知道它可以容納任何對象。

這與您在非泛型示例代碼中所做的完全不同,您在其中說過:將此String視爲Double。你只是想說:把這個(只包含數字的列表)當作一個(包含任何東西的列表)。它不會,編譯器可以檢測到它,所以它不會讓你擺脫它。

+0

很好的解釋,謝謝。 – 2009-08-06 17:55:52

1

"What we are doing here is essentially the same thing, except this will pass compile-time checks and only fail at run-time. The version with Lists won't compile."

您看到的是什麼是非常合情合理的,當你認爲Java泛型的主要目的是讓不兼容的類型在編譯時,而不是運行時失敗。

java.sun.com

Generics provides a way for you to communicate the type of a collection to the compiler, so that it can be checked. Once the compiler knows the element type of the collection, the compiler can check that you have used the collection consistently and can insert the correct casts on values being taken out of the collection.

0

在Java中,List<S>不是List<T>亞型時ST一個亞型。此規則提供類型安全性。

比方說,我們允許List<String>List<Object>的子類型。考慮下面的例子:

public void foo(List<Object> objects) { 
    objects.add(new Integer(42)); 
} 

List<String> strings = new ArrayList<String>(); 
strings.add("my string"); 
foo(strings); // this is not allow in java 
// now strings has a string and an integer! 
// what would happen if we do the following...?? 
String myString = strings.get(1); 

所以,強制這提供了類型安全,但它也有一個缺點,它不夠靈活。請看下面的例子:

class MyCollection<T> { 
    public void addAll(Collection<T> otherCollection) { 
     ... 
    } 
} 

在這裏,你有T的集合,你想從另一個集合添加的所有項目。對於S子類型T,您不能通過Collection<S>調用此方法。理想情況下,這是可以的,因爲您只是將元素添加到集合中,而不是修改參數集合。

爲了解決這個問題,Java提供了他們所謂的「通配符」。通配符是一種提供協變/逆變的方式。現在考慮下面的使用通配符:

class MyCollection<T> { 
    // Now we allow all types S that are a subtype of T 
    public void addAll(Collection<? extends T> otherCollection) { 
     ... 

     otherCollection.add(new S()); // ERROR! not allowed (Here S is a subtype of T) 
    } 
} 

現在,使用通配符我們允許在T型協方差和我們阻止不是類型安全的操作(例如添加一個項目到集合)。這樣我們可以獲得靈活性和類型安全性。

相關問題