题 类型安全:未经检查的演员


在我的spring应用程序上下文文件中,我有类似的东西:

<util:map id="someMap" map-class="java.util.HashMap" key-type="java.lang.String" value-type="java.lang.String">
    <entry key="some_key" value="some value" />
    <entry key="some_key_2" value="some value" />   
</util:map>

在java类中,实现如下:

private Map<String, String> someMap = new HashMap<String, String>();
someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");

在Eclipse中,我看到一条警告说:

类型安全:取消选中从Object转换为HashMap

我做错了什么?我该如何解决这个问题?


198
2017-11-04 16:29


起源


我想出了一个例程来实际检查转换为参数化的HashMap,这消除了未经检查的强制转换警告: 链接 我会说这是“正确的”解决方案,但它的价值是否值得商榷。 :) - skiphoppy
相关/欺骗: 如何处理未经检查的投射警告? - blahdiblah
可能重复 如何处理未经检查的投射警告? - Angelo Fuchs


答案:


好吧,首先,你是在浪费新的记忆 HashMap 创作电话。您的第二行完全忽略了对此创建的hashmap的引用,使其可用于垃圾收集器。所以,不要这样做,使用:

private Map<String, String> someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");

其次,编译器抱怨你将对象强制转换为 HashMap 不检查它是否是 HashMap。但是,即使你这样做:

if(getApplicationContext().getBean("someMap") instanceof HashMap) {
    private Map<String, String> someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");
}

您可能仍会收到此警告。问题是, getBean 回报 Object,所以不知道是什么类型。将其转换为 HashMap 直接不会导致第二种情况的问题(也许在第一种情况下不会出现警告,我不确定Java编译器对Java 5的警告是多么迂腐)。但是,您正在将其转换为 HashMap<String, String>

HashMaps实际上是将对象作为键并将对象作为值的映射, HashMap<Object, Object> 如果你愿意的话。因此,无法保证当你获得bean时它可以表示为a HashMap<String, String> 因为你可以拥有 HashMap<Date, Calendar> 因为返回的非泛型表示可以包含任何对象。

如果代码编译,您就可以执行 String value = map.get("thisString"); 没有任何错误,不要担心这个警告。但是如果地图不完全是字符串键的字符串键,你将得到一个 ClassCastException 在运行时,因为泛型不能阻止这种情况发生。


219
2017-11-04 16:44



这是前一段时间,但我正在寻找关于在演员表之前检查Set <CustomClass>的类型的答案,并且你不能在参数化泛型上实例化。例如if(event.getTarget instanceof Set <CustomClass>)你只能用?键入check一个泛型?并且不会删除强制警告。例如if(event.getTarget instanceof Set <?>) - garlicman


问题是转换是运行时检查 - 但由于类型擦除,在运行时实际上没有区别 HashMap<String,String> 和 HashMap<Foo,Bar> 对于任何其他 Foo 和 Bar

使用 @SuppressWarnings("unchecked") 抓住你的鼻子。哦,Java中的具体化泛型运动:)


264
2017-11-04 17:09



我将把Java的具体化泛型与无类型的NSMutableWhatever相提并论,这感觉就像是一周的任何一天倒退十年。至少Java正在尝试。 - Dan Rosenstark
究竟。如果你坚持进行类型检查,它只能用HashMap <?,?>来完成,并且不会删除警告,因为它不是类型检查泛型类型。这不是世界的尽头,但令人讨厌的是你被抓住要么压制警告要么与它一起生活。 - garlicman
@JonSkeet什么是具体的通用? - SasQ
@SasQ: stackoverflow.com/questions/879855 - Jon Skeet


如上面的消息所示,List之间无法区分 List<Object> 和a List<String> 要么 List<Integer>

我已经解决了类似问题的错误消息:

List<String> strList = (List<String>) someFunction();
String s = strList.get(0);

以下内容:

List<?> strList = (List<?>) someFunction();
String s = (String) strList.get(0);

说明:第一个类型转换验证对象是否为List而不关心其中的类型(因为我们无法在List级别验证内部类型)。现在需要第二次转换,因为编译器只知道List包含某种对象。这将在访问时验证List中每个对象的类型。


59
2017-11-14 21:46



你是我的朋友。而不是强制转换列表,只需迭代它并投射每个元素,警告就不会出现,真棒。 - juan Isaza
这删除了警告,但我仍然不自信:P - mumair


警告就是这样。一个警告。有时警告是无关紧要的,有时它们不是。它们习惯于引起你注意编译器认为可能存在问题的东西,但可能不是。

在演员阵容的情况下,在这种情况下总是会发出警告。如果您完全确定特定的强制转换是安全的,那么您应该考虑在行之前添加这样的注释(我不确定语法):

@SuppressWarnings (value="unchecked")

22
2017-11-04 16:44



-1:永远不要接受警告。或者压制这些警告或修复它。在你需要许多警告的那一刻,你将不会看到相关的一次。 - ezdazuzena
在构建参数化泛型(即Map)时,您无法真正避免类强制转换警告,因此这是原始问题的最佳答案。 - muttonUp


您收到此消息是因为getBean返回一个Object引用,并且您将其转换为正确的类型。 Java 1.5为您提供警告。这就是使用Java 1.5或更高版本的代码的性质。 Spring有类型安全版本

someMap=getApplicationContext().getBean<HashMap<String, String>>("someMap");

在它的待办事项清单上。


9
2017-11-04 16:44





如果你真的想摆脱警告,你可以做的一件事是创建一个从泛型类扩展的类。

例如,如果您正在尝试使用

private Map<String, String> someMap = new HashMap<String, String>();

你可以创建一个这样的新类

public class StringMap extends HashMap<String, String>()
{
    // Override constructors
}

然后当你使用

someMap = (StringMap) getApplicationContext().getBean("someMap");

编译器知道(不再是通用)类型是什么,并且不会有警告。这可能并不总是完美的解决方案,有些人可能会认为这种方法违背了泛型类的目的,但是你仍然在重复使用泛型类中的所有相同代码,你只是在编译时声明什么类型你想用。


5
2018-04-13 15:55





另一种解决方案,如果您发现自己经常使用相同的对象并且不想丢弃代码 @SupressWarnings("unchecked"),将创建一个带注释的方法。这样你就可以集中演员阵容,并希望减少错误的可能性。

@SuppressWarnings("unchecked")
public static List<String> getFooStrings(Map<String, List<String>> ctx) {
    return (List<String>) ctx.get("foos");
}

1
2017-10-20 19:25





下面的代码导致类型安全警告

Map<String, Object> myInput = (Map<String, Object>) myRequest.get(); 

解决方法

创建新的Map对象而不提及参数,因为未验证列表中保留的对象类型。

步骤1: 创建一个新的临时地图

Map<?, ?> tempMap = (Map<?, ?>) myRequest.get();

第2步: 实例化主地图

Map<String, Object> myInput=new HashMap<>(myInputObj.size());

第3步: 迭代临时Map并将值设置到主Map中

 for(Map.Entry<?, ?> entry :myInputObj.entrySet()){
        myInput.put((String)entry.getKey(),entry.getValue()); 
    }

0
2017-12-16 14:23