这是一个精心设计的例子来说明这个问题。我知道有一些方法不会生成编译器警告,也可以禁用警告。我想知道如果没有这些技巧,这是否可行。
给定此代码:
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
除非您正在处理遗留代码(非通用(,否则未检查的强制转换应被视为破解/解决方案。