繁体   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