[英]What is unchecked cast and how do I check it?
我想我明白了 unchecked cast 的意思(從一個不同類型的轉換到另一個類型),但是“檢查”轉換是什么意思? 如何檢查演員表,以便在 Eclipse 中避免此警告?
詳細說明彼得所寫的內容:
從非泛型類型到泛型類型的轉換在運行時可能工作得很好,因為泛型參數在編譯期間被刪除,所以我們留下了一個合法的轉換。 但是,由於對類型參數的假設不正確,代碼稍后可能會失敗並出現意外的ClassCastException
。 例如:
List l1 = new ArrayList();
l1.add(33);
List<String> l2 = (List<String>) l1;
String s = l2.get(0);
第 3 行的 unchecked 警告表明編譯器無法保證類型安全,因為稍后可能會發生意外的 ClassCastException。 事實上,這發生在第 4 行,它執行了一個隱式轉換。
未經檢查的強制轉換意味着您(隱式或顯式)從泛型類型轉換為非限定類型,或者相反。 例如這條線
Set<String> set = new HashSet();
會產生這樣的警告。
通常此類警告是有充分理由的,因此您應該嘗試改進代碼而不是抑制警告。 引自 Effective Java,第 2 版:
盡可能消除所有未經檢查的警告。 如果您消除了所有警告,您就可以確信您的代碼是類型安全的,這是一件非常好的事情。 這意味着您不會在運行時收到
ClassCastException
,並增加您對程序按預期運行的信心。如果您無法消除警告,並且您可以證明引發警告的代碼是類型安全的,那么(並且只有這樣)使用
@SuppressWarnings("unchecked")
注釋抑制警告。 如果你在沒有首先證明代碼是類型安全的情況下抑制警告,你只會給自己一種錯誤的安全感。 代碼可以編譯而不發出任何警告,但它仍然可以在運行時拋出ClassCastException
。 但是,如果您忽略您知道是安全的未經檢查的警告(而不是抑制它們),您將不會注意到何時出現代表真正問題的新警告。 新警告將在您沒有靜音的所有誤報中消失。
當然,消除警告並不總是像上面的代碼那樣容易。 如果沒有看到您的代碼,就無法知道如何使其安全。
與已檢查的強制轉換相反,未檢查的強制轉換不會在運行時檢查類型安全。
這是一個基於第三版的Consider typesafe heterogenous containers
部分的示例。 Joshua Bloch 的“Effective Java”,但容器類被故意破壞 - 它存儲並返回錯誤的類型:
public class Test {
private static class BrokenGenericContainer{
private final Map<Class<?>, Object> map= new HashMap<>();
public <T> void store(Class<T> key, T value){
map.put(key, "broken!"); // should've been [value] here instead of "broken!"
}
public <T> T retrieve(Class<T> key){
// return key.cast(map.get(key)); // a checked cast
return (T)map.get(key); // an unchecked cast
}
}
public static void main(String[] args) {
BrokenGenericContainer c= new BrokenGenericContainer();
c.store(Integer.class, 42);
List<Integer> ints = new ArrayList<>();
ints.add(c.retrieve(Integer.class));
Integer i = ints.get(0);
}
}
如果retrieve()
使用未經檢查的強制轉換- (T)map.get(key)
- 運行此程序將導致在Integer i = ints.get(0)
行發生ClassCastException
。 retrieve()
方法將完成,因為在運行時未檢查實際類型:
Exception in thread "main"
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at Test.main(Test.java:27)
但是如果retrieve()
使用一個checked cast - key.cast(map.get(key))
- 運行這個程序將導致在key.cast(map.get(key))
行發生ClassCastException
,因為checked cast發現類型錯誤並拋出異常。 retrieve()
方法不會完成:
Exception in thread "main" java.lang.ClassCastException:
Cannot cast java.lang.String to java.lang.Integer
at java.lang.Class.cast(Class.java:3369)
at Test$BrokenGenericContainer.retrieve(Test.java:16)
at Test.main(Test.java:26)
看起來差別不大,但在未經檢查的強制轉換的情況下,一個String
成功地進入了一個List<Integer>
。 在現實世界的應用中,其后果可能是……好吧,嚴重。 如果使用已檢查的強制轉換,則會盡早發現類型不匹配。
為了避免未經檢查的強制轉換警告,可以使用@SuppressWarnings("unchecked")
,如果程序員真的確定該方法實際上是安全的。 更好的選擇是在可能的情況下使用泛型和檢查強制轉換。
正如約書亞·布洛赫所說,
...未經檢查的警告很重要。 不要忽視它們。
為完整起見,此答案涉及 Eclipse 細節。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.