将不一致类型的对象放入初始化的Map中-预期的和合法的



今天,我发现即使对象不能转换为正确的类型,也可以put现有Map中的对象。

首先,让我从一个简单的例子开始:

Map<Integer, String> myMap = new HashMap<>(); //plain old hashmap
myMap.put(9,"star"); //no problem
myMap.put(10, 1.2); //Incompatible type, the compiler yells
Map<Integer, Double> aMap = (Map<Integer, Double>) myMap; //Cannot cast, the compiler yells

到目前为止,一切都是意料之中的,因为您应该而不是能够将不一致类型的对象放入已经构建的Map中。现在让我们考虑一下:

public class NoRulesForMe {
static Object theRing;
public static void main(String[] args){
Map<Integer, String> myMap = new HashMap<>();
myMap.put(9,"star");
Map<Integer, Double> myMapMorphed = castWildly(myMap);
myMapMorphed.put(99, 3.14);
System.out.println(myMapMorphed.get(9)); //"star", as we put in
System.out.println(myMapMorphed.get(99)); //3.14, as we put in
}
public static <T> T castWildly(Object value){
theRing = value;
T morphed = (T) theRing;
return morphed;
}
}

令我惊讶的是,这并没有导致运行时错误——Map是如何实现这一点的?这种行为是在JLS或API中指定的,因此可以依赖吗?

我问的原因是,我在生产代码中看到了一个(更复杂的)版本,我想知道,即使这可能令人困惑和发臭,它是否可以保证在功能方面工作。任何意见都将不胜感激。

这种类型的编码风险很大!!尽管它会编译,但您会注意到编译器会发出警告:

注意:NoRulesForMe.java使用未检查或不安全的操作
注意:使用-Xlint重新编译:有关详细信息,请取消选中。

这些警告,尤其是在使用泛型的情况下,永远不应该被忽略,也不应该被抑制。您必须绝对确保(从逻辑上遵循代码)转换是安全的,以后不会引起一些问题。最好始终以这样的方式进行编码,即在编译器时而不是运行时发现并拾取错误。编译器在这里给出的警告告诉你,事情可能会出错。

您将把myMap作为Object传递给方法castWildly,当您进行强制转换时,您将从Object强制转换为Map

编译器可以推断出代码中的T具有Map<String, Double>的类型目标,因此可以推断出这一点。然而,在强制转换时,它没有关于Object value(或Object theRing)是什么(子)类型的信息。因此,它无法检查强制转换是否安全(特别是类型安全)。

当您从映射中检索值时,就会出现此代码的问题。下面的代码增加了一行,并且代码进行了编译(带有与上面相同的警告)。这是因为当从声明为Map<String, Double>的映射中检索值作为Double时,当编译器进行类型检查时,它是绝对有效的。。。然而,在运行时,您的代码将崩溃(运行时崩溃错误如下所示)。这是一种非常危险的编码方式,尤其是在生产代码中。你宁愿让你的编译器给你错误,也不愿部署编译并在运行时导致产品崩溃的生产代码。

public class NoRulesForMe {
static Object theRing;
public static void main(String[] args){
Map<Integer, String> myMap = new HashMap<>();
myMap.put(9,"star");
Map<Integer, Double> myMapMorphed = castWildly(myMap);
myMapMorphed.put(99, 3.14);
System.out.println(myMapMorphed.get(9)); //"star", as we put in
System.out.println(myMapMorphed.get(99)); //3.14, as we put in
// added to show why this style of coding causes problems
Double testValue1 = myMapMorphed.get(9);
}
public static <T> T castWildly(Object value){
theRing = value;
T morphed = (T) theRing;
return morphed;
}
}

运行上述代码时出现运行时错误:

star
3.14
线程"main"java.lang.ClassCastException异常:java.lang.String无法强制转换为java.lang.Double在NoRulesForMe.main(NoRulesForMe.java:19)

有关更多信息,请阅读约书亚·布洛赫的《高效Java》;项目24:消除未检查的警告。(此项目位于Generics标题下)。

相关内容

最新更新