簡體   English   中英

調用`clone()`時是否可以避免未經檢查的強制轉換?

[英]Is it possible to avoid unchecked casting when calling `clone()`?

這是一個人為的例子來說明這個問題。 我知道有一些方法不會產生編譯器警告,您也可以禁用警告。 我想知道如果沒有任何這些技巧,這是否可能。

鑒於此代碼:

1 public static void main(String[] args) {
2    Map<String,String> map = null;
3
4    HashMap<String,String> hmap;
5
6    if(map instanceof HashMap)
7        hmap = (HashMap<String,String>)map;
8    else
9        hmap = new HashMap<String,String>(map);
10
11   map = (Map<String,String>)hmap.clone();
12
13    Object o = hmap.clone();
14    if(o instanceof Map<?,?>)
15        map = (Map<String,String>)o;
16 }

第 11 行第 15 行的代碼都會生成編譯器警告:

Unchecked cast from Object to Map<String,String>

第 11 行有點可以理解: Object.clone()返回Object ,並且在轉換之前沒有進行instanceof檢查。 程序員知道克隆將是Map<String,String> ,但編譯器無法證明這一點。

不過,第 15 行讓我感到困惑。 通常,使用instanceof檢查變量的類型然后立即轉換它不會產生這樣的警告。 事實上,用這樣的非參數化類替換代碼將不會在以下任何代碼行中產生警告:

static class A {}
static class B extends A implements Cloneable {
    public Object clone() { return null; }
}
public static void main(String[] args) {
    A a = null;

    B b;

    if(a instanceof B)
        b = (B)a;
    else
        b = new B();

    a = (A)b.clone();

    Object o = b.clone();
    if(o instanceof A)
        a = (A)o;
}

回到原始代碼(使用Map<String,String>引用),即使在代碼末尾添加這個笨拙的結構也會產生類似的警告:

map = (Map<String,String>)hmap.getClass().cast(o);

這次的警告是Unchecked cast from capture#11-of? extends HashMap to Map<String,String> Unchecked cast from capture#11-of? extends HashMap to Map<String,String> 試圖寫:

map = HashMap<String,String>.class.cast(o);

Generates a compiler error because it can't figure out that HashMap<String,String>.class is a static class reference in the same way that eg HashMap.class , so we have to use a reference of the "correct" type to call Class.cast

這是 Java 做不到的嗎?

這是 Java 做不到的嗎?

是的,它就是這樣設計的。

看HashMap克隆方法的java源碼(1.8):

@SuppressWarnings("unchecked")
@Override
public Object clone() {
    HashMap<K,V> result;
    try {
        result = (HashMap<K,V>)super.clone();
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
    result.reinitialize();
    result.putMapEntries(this, false);
    return result;
}

它使用@SuppressWarnings("unchecked")來抑制super.clone()上的警告。

你不能完全避免它,但如果你的代碼有很多 clone() 方法,你可以通過提取到方法來最小化這些警告:

@SuppressWarnings("unchecked")
HashMap<String,String> cloneWithoutWarning(HashMap<String,String> map) { return (HashMap<String,String>) map.clone(); } 

通常,使用 instanceof 檢查變量的類型然后立即轉換它不會產生這樣的警告。

這不是真的。 instanceof對強制轉換警告沒有影響。

這不是關於此演員陣容中可能出現的ClassCastException的警告。 未經檢查的演員表意味着 Java 無法安全地進行演員表。 這意味着 cast 可能會在沒有ClassCastException的情況下通過,但類型不匹配。 這可能會導致來自意外地方的ClassCastException

在這種情況下,這是一個真正的問題。 HashMap.clone()返回Object以實現向后兼容性。 沒有辦法判斷它的實現是否是類型安全的。 例如:

import java.util.*;
class Main {
    public static void main(String[] args) {
        HashMap<String,String> map = new HashMap() {
            @Override
            public Object clone() {
                HashMap o = (HashMap)super.clone();
                o.put("x", new Object());
                return o;
            }
        };
        Map<String,String> copy = (Map<String,String>)map.clone(); // will pass
        System.out.println(copy.get("x")); // no cast but fails with ClassCastException
    }
}

clone()是有問題的。 如果可能,只需創建一個新的HashMap

如果您必須使用克隆,請僅使用安全強制轉換:

Map<?, ?> copy = (Map<?, ?>)map.clone();
System.out.println((String)copy.get("x")); // explicit cast will fail

除非您正在處理遺留代碼(非通用),否則應將未經檢查的強制轉換視為 hacks/workarounds。

暫無
暫無

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

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