简体   繁体   English

调用`clone()`时是否可以避免未经检查的强制转换?

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

This is a contrived example to illustrate the problem.这是一个人为的例子来说明这个问题。 I know there are ways around this that don't generate compiler warnings and you can also disable the warning.我知道有一些方法不会产生编译器警告,您也可以禁用警告。 I'm wondering if this is possible without any of those tricks.我想知道如果没有任何这些技巧,这是否可能。

Given this code:鉴于此代码:

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 }

The code on both lines 11 and 15 generate the compiler warning:第 11 行第 15 行的代码都会生成编译器警告:

Unchecked cast from Object to Map<String,String>

Line 11 is a little understandable: Object.clone() returns an Object , and there has been no instanceof check prior to casting.第 11 行有点可以理解: Object.clone()返回Object ,并且在转换之前没有进行instanceof检查。 The programmer knows that the clone will be a Map<String,String> , but the compiler can't prove it.程序员知道克隆将是Map<String,String> ,但编译器无法证明这一点。

Line 15 is puzzling to me, though.不过,第 15 行让我感到困惑。 Usually, checking the type of a variable using instanceof and then immediately casting it will not generate such a warning.通常,使用instanceof检查变量的类型然后立即转换它不会产生这样的警告。 Indeed, replacing the code with non-parameterized classes like this will generate no warnings, on either of these lines of code:事实上,用这样的非参数化类替换代码将不会在以下任何代码行中产生警告:

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;
}

Back to the original code (with the Map<String,String> references), even adding this awkward construction to the end of the code generates a similar warning:回到原始代码(使用Map<String,String>引用),即使在代码末尾添加这个笨拙的结构也会产生类似的警告:

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

The warning this time is Unchecked cast from capture#11-of? extends HashMap to Map<String,String>这次的警告是Unchecked cast from capture#11-of? extends HashMap to Map<String,String> Unchecked cast from capture#11-of? extends HashMap to Map<String,String> . Unchecked cast from capture#11-of? extends HashMap to Map<String,String> Trying to write:试图写:

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 . 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

Is this something that Java just can't do?这是 Java 做不到的吗?

Is this something that Java just can't do?这是 Java 做不到的吗?

Yes, it's how it is by design.是的,它就是这样设计的。

Look at java sources (1.8) of HashMap clone method:看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;
}

It uses @SuppressWarnings("unchecked") for the same purposes to suppress warning on super.clone() .它使用@SuppressWarnings("unchecked")来抑制super.clone()上的警告。

You can not completely avoid it but if your code has a lot of clone() methods you can minimize these warnings by extracting to method like:你不能完全避免它,但如果你的代码有很多 clone() 方法,你可以通过提取到方法来最小化这些警告:

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

Usually, checking the type of a variable using instanceof and then immediately casting it will not generate such a warning.通常,使用 instanceof 检查变量的类型然后立即转换它不会产生这样的警告。

This is not true.这不是真的。 instanceof s have no effect on casting warnings. instanceof对强制转换警告没有影响。

This is not a warning about possible ClassCastException from this cast.这不是关于此演员阵容中可能出现的ClassCastException的警告。 Unchecked cast means Java cannot do the cast safely.未经检查的演员表意味着 Java 无法安全地进行演员表。 That means cast might pass without ClassCastException yet the type is not a match.这意味着 cast 可能会在没有ClassCastException的情况下通过,但类型不匹配。 This might result in ClassCastException from unexpected place.这可能会导致来自意外地方的ClassCastException

This is a real issue in this case.在这种情况下,这是一个真正的问题。 HashMap.clone() returns Object for backwards compatibility. HashMap.clone()返回Object以实现向后兼容性。 There is no way to tell, if its implementation is type safe.没有办法判断它的实现是否是类型安全的。 For example:例如:

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() is problematic. clone()是有问题的。 If possible, just create a new HashMap .如果可能,只需创建一个新的HashMap

If you have to use clone only use safe casts:如果您必须使用克隆,请仅使用安全强制转换:

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

Unless you are dealing with legacy code (non-generic) unchecked casts should be considered hacks/workarounds.除非您正在处理遗留代码(非通用),否则应将未经检查的强制转换视为 hacks/workarounds。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM