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

Dra*_*orn 248 java spring unchecked type-safety

在我的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>
Run Code Online (Sandbox Code Playgroud)

在java类中,实现如下:

private Map<String, String> someMap = new HashMap<String, String>();
someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");
Run Code Online (Sandbox Code Playgroud)

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

类型安全:从Object到HashMap的未选中转换

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

Jon*_*eet 303

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

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

  • 我将把Java的具体化泛型与无类型的NSMutableWhatever相提并论,这就像是一周中任何一天的倒退十年.至少Java正在尝试. (14认同)
  • 究竟.如果你坚持进行类型检查,它只能用HashMap <?,?>来完成,并且不会删除警告,因为它与不检查泛型类型的类型相同.这不是世界的尽头,但令人烦恼的是你被抓住要么压制警告要么与它一起生活. (12认同)
  • @JonSkeet什么是具体的通用? (4认同)

Met*_*002 240

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

private Map<String, String> someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");
Run Code Online (Sandbox Code Playgroud)

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

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

您可能仍会收到此警告.问题是,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在运行时得到一个,因为泛型不能阻止这种情况发生.

  • 这是前一段时间,但我在寻找一个类型检查一个Set <CustomClass>之前的答案,并且你不能在参数化泛型上实例化.例如if(event.getTarget instanceof Set <CustomClass>)你只能用一个?键入check a generic?并且不会删除演员警告.例如if(event.getTarget instanceof Set <?>) (11认同)

小智 79

如上面的消息所示,列表无法区分a List<Object>和a List<String>List<Integer>.

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

List<String> strList = (List<String>) someFunction();
String s = strList.get(0);
Run Code Online (Sandbox Code Playgroud)

以下内容:

List<?> strList = (List<?>) someFunction();
String s = (String) strList.get(0);
Run Code Online (Sandbox Code Playgroud)

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

  • 这消除了警告,但我仍然没有信心:P (3认同)
  • 是的,感觉像是蒙住了编译器而不是运行时 :D 所以我看不出这和 @SuppressWarnings("unchecked") 有什么区别 (3认同)
  • 你是对的我的朋友。除了强制转换列表并强制转换每个元素外,警告不会出现,这太棒了。 (2认同)
  • 棒极了!使用 @SupressWarning 的主要区别在于它使用注释消除了来自 IDE 和代码分析工具的警告,但如果您使用 -Werror 标志编译,您最终仍会出错。使用这种方法可以修复两个警告。 (2认同)
  • 在 https://www.baeldung.com/java-warning-unchecked-cast 中,解释了 ClassCastException 可能会在代码中的任何位置抛出,而不参考这个令人讨厌的未经检查的强制转换。解决方法是将强制转换移动到具有非常简洁的 ClassCastException 的内容,因此如果需要,可以更轻松地修复。 (2认同)

Dav*_*arr 28

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

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

@SuppressWarnings (value="unchecked")
Run Code Online (Sandbox Code Playgroud)

  • -1:永远不应接受警告.或者压制这些警告或修复它.在你需要许多警告的那一刻,你将不会看到相关的一次. (13认同)
  • 在构建参数化泛型(即Map)时,您无法真正避免类强制转换警告,因此这是原始问题的最佳答案. (8认同)
  • 呵呵。很高兴有人对我 14 年前给出的这个答案发表评论。我再也不会这样做了。有时警告实际上无法修复,但我宁愿看到它们也不愿隐藏它们。 (2认同)

Dav*_*hme 9

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

someMap=getApplicationContext().getBean<HashMap<String, String>>("someMap");
Run Code Online (Sandbox Code Playgroud)

在它的待办事项清单上.


och*_*kov 6

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

class MyMap extends HashMap<String, String> {};
someMap = (MyMap)getApplicationContext().getBean("someMap");
Run Code Online (Sandbox Code Playgroud)

  • 看起来像是一个黑客而不是解决方案。 (3认同)

Rab*_*bit 5

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

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

private Map<String, String> someMap = new HashMap<String, String>();
Run Code Online (Sandbox Code Playgroud)

您可以创建这样的新类

public class StringMap extends HashMap<String, String>()
{
    // Override constructors
}
Run Code Online (Sandbox Code Playgroud)

然后当你使用

someMap = (StringMap) getApplicationContext().getBean("someMap");
Run Code Online (Sandbox Code Playgroud)

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