调用"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>。尝试写入:

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

生成编译器错误,因为它无法像HashMap.class那样确定HashMap<String,String>.class是静态类引用,所以我们必须使用"正确"类型的引用来调用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无法安全地执行强制转换。这意味着转换可能在没有ClassCastException的情况下通过,但类型不匹配。这可能导致ClassCastException来自意外的位置。

在这种情况下,这是一个真正的问题。CCD_ 17返回用于向后兼容性的CCD_ 18。没有办法判断它的实现是否是类型安全的。例如:

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

CCD_ 19是有问题的。如果可能,只需创建一个新的HashMap

如果必须使用克隆,请仅使用安全强制转换:

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

除非您正在处理遗留代码(非通用(,否则未检查的强制转换应被视为破解/解决方案。

相关内容

  • 没有找到相关文章

最新更新