调用 clone() 时是否可以避免未经检查的转换?
Is it possible to avoid unchecked casting when calling `clone()`?
这是一个人为的例子来说明问题。我知道有 ways around this 不会生成编译器警告,您也可以禁用该警告。我想知道如果没有这些技巧,这是否可行。
鉴于此代码:
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()
returns 和 Object
,并且在转换之前没有进行 instanceof
检查。程序员 知道 克隆将是 Map<String,String>
,但编译器无法证明它。
不过,第 15 行让我感到困惑。通常,使用 instanceof
检查变量的类型然后立即转换它不会产生这样的警告。实际上,像这样用非参数化 classes 替换代码将不会在以下代码行中的任何一行上产生警告:
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<String,String>.class
是一个静态 class 引用,其方式与例如HashMap.class
,所以我们要用"correct"类型的引用来调用Class.cast
.
这是Java做不到的吗?
Is this something that Java just can't do?
是的,设计就是这样。
查看 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(); }
Usually, checking the type of a variable using instanceof and then immediately casting it will not generate such a warning.
这不是真的。 instanceof
s 对施放警告没有影响。
这不是关于此演员可能 ClassCastException
的警告。未经检查的转换意味着 Java 无法安全地进行转换。这意味着转换可能在没有 ClassCastException
的情况下通过,但类型不匹配。这可能会导致 ClassCastException
来自意想不到的地方。
在这种情况下这是一个真正的问题。 HashMap.clone()
returns 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。
这是一个人为的例子来说明问题。我知道有 ways around this 不会生成编译器警告,您也可以禁用该警告。我想知道如果没有这些技巧,这是否可行。
鉴于此代码:
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()
returns 和 Object
,并且在转换之前没有进行 instanceof
检查。程序员 知道 克隆将是 Map<String,String>
,但编译器无法证明它。
不过,第 15 行让我感到困惑。通常,使用 instanceof
检查变量的类型然后立即转换它不会产生这样的警告。实际上,像这样用非参数化 classes 替换代码将不会在以下代码行中的任何一行上产生警告:
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<String,String>.class
是一个静态 class 引用,其方式与例如HashMap.class
,所以我们要用"correct"类型的引用来调用Class.cast
.
这是Java做不到的吗?
Is this something that Java just can't do?
是的,设计就是这样。
查看 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(); }
Usually, checking the type of a variable using instanceof and then immediately casting it will not generate such a warning.
这不是真的。 instanceof
s 对施放警告没有影响。
这不是关于此演员可能 ClassCastException
的警告。未经检查的转换意味着 Java 无法安全地进行转换。这意味着转换可能在没有 ClassCastException
的情况下通过,但类型不匹配。这可能会导致 ClassCastException
来自意想不到的地方。
在这种情况下这是一个真正的问题。 HashMap.clone()
returns 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。