2012-12-04 25 views
2

我有這樣的代碼,它不能通過編譯:爲什麼當一個類變得通用時,「不兼容類型」編譯錯誤會變成警告?

import java.util.ArrayList; 
import java.util.List; 

public class TypeSafetyTest { 

    public static void main(String[] args) { 
     TypeSafetyTest test = new TypeSafetyTest(); 
     test.run(); 
    } 

    private void run() { 
     Car car = new Car(); 
     List<String> wheelWeights = car.getWheelWeights(); 
    } 

    private class Car { 
     List<Double> wheelWeights = new ArrayList<Double>(); 

     public List<Double> getWheelWeights() { 
      return wheelWeights; 
     } 

     public void setWheelWeights(List<Double> wheelWeights) { 
      this.wheelWeights = wheelWeights; 
     } 
    } 
} 

它給上線的 「不兼容類型的」 錯誤:

List<String> wheelWeights = car.getWheelWeights(); 

不過,如果我更改行:

private class Car { 

private class Car<T> { 

然後代碼編譯成功,並在過去有編譯錯誤的行上發出警告「Unchecked assignment」。這是爲什麼?我期待在這兩種情況下都會出現編譯錯誤。

+0

試圖瞭解Java泛型只會導致瘋狂。我一直在瀏覽Java語言規範,但我仍然無法弄清這個問題的答案。 – Antimony

+0

不重複,但密切相關:[什麼是原始類型,爲什麼我們不應該使用它?](http://stackoverflow.com/questions/2770321/what-is-a-raw-type-and- why-shouldnt-we-use-it)基本上,一旦你使用原始類型聲明你的類,你已經明確地選擇了整個類的所有泛型。 –

回答

3

一個問題,我看到的是:

public List<Double> getWheelWeights() { 
      return wheelWeights; 
     } 

回報DoubleList,但你將其分配給List<String>

你之所以沒有看到當你改變Car<T>編譯錯誤,因爲你的實例創建是原始類型,它告訴編譯器在編譯時忽略類型安全性,這是由於向後兼容性。

Car<Object> car = new Car<Object>(); 

如果你像這樣改變你的對象,那麼你會看到編譯器再次拋出消息。

欲瞭解更多信息請參閱JLS 4.8

這裏是例如從JLS:

class Outer<T>{ 
     class Inner<S> { 
      S s; 
     } 
    } 

Outer.Inner<Double> x = null; // illegal 

這是不可能訪問內作爲部分原料類型(「罕見」型),因爲外本身是生的,因此這樣是它的所有內部類包括Inner,因此不可能將任何類型參數傳遞給Inner

只允許使用原始類型作爲遺留代碼兼容性的讓步。 強烈建議在將泛型引入Java編程語言之後編寫的代碼中使用原始類型。未來版本的Java編程語言可能會禁止使用原始類型。

我建議閱讀Generics (Updated)

+0

他知道......他問他爲什麼在使用參數類型時不會出錯。我的回答完全是一個受過教育的猜測。我想你可以給出更正確的答案。 –

+0

我明白了。如果編譯器真的忽略了類型安全性,因爲Car對象沒有使用類型初始化,那麼我希望編譯器只會忽略Car中實際使用類型聲明的字段的類型安全性。 – Thomas

+1

@Thomas原始類型基本上是遺留代碼的兼容性度量,因此應用的類型檢查很少。 – Antimony

3

當您更改CarCar<T>Car成爲一個通用類。因此,您的car對象現在具有原始類型Car,因爲您沒有提供任何類型參數。原始類型的非靜態非繼承成員也是原始類型。這意味着getWheelWeights()現在正在返回一個原始類型。而且,由於它是原始類型,因此它會進行未經檢查的轉換,這意味着您只會收到警告,而不是錯誤。

您可以通過爲car提供一個類型參數來解決此問題。例如:

Car<Object> car = new Car(); 

請注意,如果您想要泛型的類型安全性好處,則根本不應使用原始類型。它們只是針對預先通用代碼的兼容性修補程序,因此對原始類型的傳染性和缺少檢查。

+3

您的「修復」仍然會導致未經檢查的轉換警告,因爲右側仍然是原始類型(但至少使用'car'變量不會)。更好的是在雙方指定類型參數,如下所示:'汽車汽車=新車();'。 –

+0

@丹尼爾好點。我的意思是「修復」,使編譯器錯誤返回,但你是對的,最好在雙方都使用類型參數。 – Antimony

相關問題