今天,我发现即使对象无法转换为正确的类型,也能够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
。
首先,让我从一个简单的例子开始:
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中。现在让我们考虑一下:
file = open("createdFile.txt", "w")
file.write("User Data Number:\n")
file.write("10101")
file.close()
file = open("createdFile.txt", "r")
getUserNumber = file.readline(2)
print(getUserNumber)
file.close()
我很惊讶这并没有导致运行时错误 - Map如何实现这一点,这种行为是在JLS或API中指定的,因此可以依赖它?
我问的原因是我在生产代码中看到了这个(更多涉及的)版本,我想知道,即使这可能是令人困惑和臭,也可以保证它在功能方面起作用。任何意见都将不胜感激。
答案 0 :(得分:2)
这种编码风险很大!!虽然它会编译,但你会注意到编译器抱怨警告:
注意:NoRulesForMe.java使用未经检查或不安全的操作。
注意:使用-Xlint重新编译:取消选中以获取详细信息。
这些警告,特别是因为您使用泛型,不应该被忽略,也不应该被抑制。您必须绝对确定(逻辑上遵循代码)演员阵容是安全的,并且以后不会引起一些问题。最好始终以这样的方式编写代码,以便在编译器时间而不是运行时发现和拾取错误。这里编译器发出的警告告诉你事情可能会出错。
您正在将myMap
作为Object
传递给方法castWildly
,当您投射时,您正在从Object
投射到Map
。
编译器可以推断出代码中的T
的类型目标为Map<String, Double>
,因此可以推断出这一点。但是,在投射时,它没有关于Object value
(或Object theRing
)的(子)类型的信息。所以它无法检查演员是否安全(特别是类型安全)。
当您从地图中检索值时,会出现此代码的问题。以下代码有一个额外添加的行,代码编译(与上面相同的警告)。这是因为当在编译器在运行时进行类型检查时,从声明为Double
的映射中检索值Map<String, 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;
}
}
运行上述代码时运行时错误:
星星 3.14
线程“main”中的异常java.lang.ClassCastException:java.lang.String无法强制转换为java.lang.Double 在NoRulesForMe.main(NoRulesForMe.java:19)
有关详细信息,请阅读Joshua Bloch撰写的Effective Java;第24项:消除未经检查的警告。 (此项目位于Generics标题下)。