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

Posted

技术标签:

【中文标题】类型安全:未经检查的演员表【英文标题】:Type safety: Unchecked cast 【发布时间】:2010-09-20 17:08:00 【问题描述】:

在我的 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,它消除了未经检查的转换警告:link 我会说这是“正确”的解决方案,但不管它是否值得商榷。 :) 相关/欺骗:How do I address unchecked cast warnings? How do I address unchecked cast warnings?的可能重复 【参考方案1】:

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

这里:

Map&lt;String,String&gt; someMap = (Map&lt;String,String&gt;)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&lt;String, String&gt; 对象。

但是&lt;T&gt; T getBean(String name, Class&lt;T&gt; 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;
    

bean 注入:

@Autowired
@Qualifier("someMap")
Map<String, String> someMap;

【讨论】:

【参考方案2】:

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

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

【讨论】:

看起来像一个黑客而不是解决方案。 - 可序列化类 MyMap 没有声明 long 类型的静态最终 serialVersionUID 字段: @Ulterior 然后添加这样一个字段:***.com/questions/2288937/…【参考方案3】:

嗯,首先,新的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&lt;String, String&gt;

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

如果代码编译成功,并且您可以执行String value = map.get("thisString"); 而不会出现任何错误,请不要担心此警告。但是,如果映射不完全由字符串键到字符串值,您将在运行时得到ClassCastException,因为在这种情况下泛型无法阻止这种情况发生。

【讨论】:

这是不久前的事了,但我一直在寻找关于在类型转换之前检查 Set 的答案,并且您不能在参数化泛型上使用 instanceof。例如if(event.getTarget instanceof Set) 您只能使用 ?这不会删除演员警告。例如if(event.getTarget instanceof Set>)【参考方案4】:

如上述消息所示,列表无法区分List&lt;Object&gt;List&lt;String&gt;List&lt;Integer&gt;

我已针对类似问题解决了此错误消息:

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 时验证每个对象的类型。

【讨论】:

你是对的,我的朋友。而不是强制转换列表,只需迭代它并强制转换每个元素,警告不会出现,太棒了。 这消除了警告,但我仍然不自信:P 是的感觉就像蒙住编译器但不是运行时:D 所以我看不出这和 @SuppressWarnings("unchecked") 有什么区别 太棒了!使用 @SupressWarning 的主要区别在于,它使用注释消除了 IDE 和代码分析工具的警告,但如果你使用 -Werror 标志编译,你最终会出错。使用这种方法,两个警告都是固定的。【参考方案5】:

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

Map&lt;String, Object&gt; myInput = (Map&lt;String, Object&gt;) myRequest.get();

解决方法

创建一个新的地图对象而不提及参数,因为列表中保存的对象类型未验证。

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

Map&lt;?, ?&gt; tempMap = (Map&lt;?, ?&gt;) 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()); 
    

【讨论】:

【参考方案6】:

另一个解决方案是,如果您发现自己经常投射相同的对象并且不想在代码中乱扔@SupressWarnings("unchecked"),则可以使用注释创建一个方法。这样您就可以集中演员表,并希望减少出错的可能性。

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

【讨论】:

【参考方案7】:

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

例如,如果您尝试使用

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

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

public class StringMap extends HashMap<String, String>()

    // Override constructors

那么当你使用

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

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

【讨论】:

【参考方案8】:

问题在于强制转换是运行时检查 - 但由于类型擦除,在运行时,HashMap&lt;String,String&gt;HashMap&lt;Foo,Bar&gt; 对于任何其他 FooBar 之间实际上没有区别。

使用@SuppressWarnings("unchecked") 并捏住鼻子。哦,还有 Java 中的物化泛型运动 :)

【讨论】:

我将采用 Java 的具体化泛型而不是无类型的 NSMutableWhatever,这感觉就像是在一周中的任何一天倒退了十年。至少 Java 正在尝试。 没错。如果您坚持进行类型检查,则只能使用 HashMap,?> 来完成,并且不会删除警告,因为它与不检查泛型类型相同。这不是世界末日,但令人讨厌的是,您要么隐瞒警告,要么忍受警告。 @JonSkeet 什么是物化泛型?【参考方案9】:

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

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

在它的待办事项列表上。

【讨论】:

【参考方案10】:

警告就是这样。一个警告。有时警告是无关紧要的,有时则不是。它们用于提醒您注意编译器认为可能是问题但可能不是问题的事情。

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

@SuppressWarnings (value="unchecked")

【讨论】:

-1:永远不应该接受警告。或抑制此类警告或修复它。有一天,您将不得不收到许多警告,而您一次也看不到相关警告。 在转换参数化泛型(即 Map)时,您无法真正避免类转换警告,因此这是原始问题的最佳答案。

以上是关于类型安全:未经检查的演员表的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin泛型未经检查的警告

生成签名 APK 时未经检查的演员表

类型安全:从 Object 到 List<MyObject> 的未经检查的强制转换

重新设计未经检查的演员警告

Java中的使用了未经检查或不安全的操作

Java 类型安全的异构容器