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


265

在我的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的强制转换

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


我想出了一个例程来实际检查对参数化HashMap的转换,从而消除了未经检查的转换警告:链接我会说这是“正确的”解决方案,但是它的价值是否值得商bat。:)
skiphoppy


Answers:


248

好吧,首先,您正在通过新的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");
}

您可能仍然会收到此警告。问题是,getBeanreturn 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在运行时会得到a ,因为在这种情况下泛型无法阻止这种情况的发生。


12
这是前一阵子,但是我一直在寻找一个在类型转换之前检查Set <CustomClass>的类型的答案,并且您不能在已参数化的泛型上使用instanceof。例如if(event.getTarget instanceof Set <CustomClass>)您只能键入带有?的检查泛型。并且不会删除强制转换警告。例如,if(event.getTarget instanceof Set <?>)
大蒜人

315

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

使用@SuppressWarnings("unchecked")并保持鼻子。哦,为Java中的泛型泛型而战:)


14
我将使用Java的通用泛型而不是无类型的NSMutableWhatever,这在一周中的任何一天都感觉像是倒退了十年。至少Java正在尝试。
Dan Rosenstark 2011年

12
究竟。如果您坚持要进行类型检查,则只能使用HashMap <?,?>来完成,并且不会删除警告,因为它与不检查常规类型的类型相同。这不是世界末日,但令人讨厌的是您被压抑或忍受警告。
大蒜人2012年

5
@JonSkeet什么是通用泛型?
SasQ '16

89

如上面的消息所示,列表不能在a 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包含某种对象。这将在访问列表时验证列表中每个对象的类型。


3
你是对的我的朋友。除了强制转换列表并强制转换每个元素外,警告不会出现,这太棒了。
伊萨扎(Juan Isaza),2015年

2
这消除了警告,但我仍然不确定:P
mumair

1
是的感觉就像蒙住了编译器,但没有运行时:D所以,我看不到它和@SuppressWarnings(“ unchecked”)之间没有任何区别
channae

1
棒极了!使用@SupressWarning的主要区别在于,它使用批注消除了IDE和代码分析工具的警告,但是如果使用-Werror标志编译,则最终会出错。使用此方法,两个警告均已修复。
Edu Costa

30

一个警告就是这样。一个警告。有时警告是无关紧要的,有时则是无关紧要的。它们通常被用来引起您对编译器认为可能是但可能不是问题的注意。

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

@SuppressWarnings (value="unchecked")

14
-1:永远不应接受警告。或禁止此类警告或对其进行修复。到时候,您将收到许多警告,而您一次也不会看到相关警告。
ezdazuzena

10
当强制转换参数化的泛型(即Map)时,您不能真正避免类强制转换警告,因此这是原始问题的最佳答案。
muttonUp

9

之所以收到此消息,是因为getBean返回对象引用,并且将其强制转换为正确的类型。Java 1.5会警告您。这就是将Java 1.5或更高版本与这样的代码一起使用的本质。Spring具有类型安全版本

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

在其待办事项清单上。


6

如果您真的想摆脱警告,您可以做的一件事就是创建一个从通用类扩展的类。

例如,如果您尝试使用

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

您可以创建一个像这样的新类

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

然后当你使用

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

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


3

避免未经检查的警告的解决方案:

class MyMap extends HashMap<String, String> {};
someMap = (MyMap)getApplicationContext().getBean("someMap");

看起来像是骇客,而不是解决方案。
Malwinder Singh

1
-可序列化的类MyMap没有声明类型为long的静态最终serialVersionUID字段:{
Ulterior

1

另一个解决方案是,如果您发现自己大量投射相同的对象,而又不想用乱扔代码@SupressWarnings("unchecked"),则可以创建带有注释的方法。这样,您就可以集中转换,并希望减少出错的可能性。

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

1

以下代码导致类型安全警告

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

解决方法

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

步骤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

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

这里 :

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

您使用的是我们通常不希望使用的旧方法,因为它会返回Object

Object getBean(String name) throws BeansException;

支持从bean工厂获取(对于单例)/创建(对于原型)bean的方法是:

<T> T getBean(String name, Class<T> requiredType) throws BeansException;

使用它,例如:

Map<String,String> someMap = app.getBean(Map.class,"someMap");

将编译,但仍带有未检查的转换警告,因为所有Map对象不一定都是Map<String, String>对象。

但是<T> T getBean(String name, Class<T> requiredType) throws BeansException;在bean泛型类(如泛型集合)中还不够,因为这需要指定多个类作为参数:集合类型及其泛型。

通常,在这种情况下,更好的方法不是直接使用BeanFactory方法,而是让框架注入Bean。

Bean声明:

@Configuration
public class MyConfiguration{

    @Bean
    public Map<String, String> someMap() {
        Map<String, String> someMap = new HashMap();
        someMap.put("some_key", "some value");
        someMap.put("some_key_2", "some value");
        return someMap;
    }
}

豆类注射剂:

@Autowired
@Qualifier("someMap")
Map<String, String> someMap;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.