Eclipse正在向我发出以下形式的警告:
类型安全:取消选中从Object转换为HashMap
这是来自对我无法控制的API的调用返回Object:
HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {
HashMap<String, String> theHash = (HashMap<String, String>)session.getAttribute("attributeKey");
return theHash;
}
不过,我已经找到了消除这个的好办法 . 我可以将涉及的单行提取到一个方法中,并将 @SuppressWarnings("unchecked")
添加到该方法,从而限制了我忽略警告的代码块的影响 . 有更好的选择吗?我不想在Eclipse中关闭这些警告 .
在我开始使用代码之前,它更简单,但仍然引发了警告:
HashMap getItems(javax.servlet.http.HttpSession session) {
HashMap theHash = (HashMap)session.getAttribute("attributeKey");
return theHash;
}
当你试图使用哈希时会出现问题:
HashMap items = getItems(session);
items.put("this", "that");
Type safety: The method put(Object, Object) belongs to the raw type HashMap. References to generic type HashMap<K,V> should be parameterized.
24 回答
在这种特殊情况下,我不会直接将Maps存储到HttpSession中,而是将我自己的类的实例存储,而后者又包含一个Map(类的实现细节) . 然后,您可以确保 Map 中的元素属于正确的类型 .
但是如果你想要检查Map的内容是否是正确的类型,你可以使用这样的代码:
问题出在这里:
session.getAttribute(...)
的结果是object
可能是任何东西,但是因为你"know"它是HashMap<String, String>
你instanceof
与instanceof
的兼容性 .警告抑制不是解决方案 . 你不应该在一个语句中进行两级转换 .
如果您确定session.getAttribute()返回的类型是HashMap,那么您不能将该类型转换为该类型,而是仅依赖于检查泛型HashMap
然后,Eclipse将会出现警告,但当然这会导致运行时错误,这些错误很难调试 . 我只在非操作关键的上下文中使用这种方法 .
拿这个,它比创建一个新的HashMap快得多,如果它已经是一个,但仍然是安全的,因为每个元素都是根据它的类型进行检查的......
在Android Studio中,如果要禁用检查,可以使用:
我可能误解了这个问题(一个例子和几个周围的行会很好),但为什么不总是使用适当的接口(和Java5)?我没有理由为什么你想要转换为
HashMap
而不是Map<KeyType,ValueType>
. 事实上,我无法想象有任何理由将变量的类型设置为HashMap
而不是Map
.为什么源是
Object
?它是旧版集合的参数类型吗?如果是这样,请使用泛型并指定所需的类型 .这是一个缩短的 example that avoids the "unchecked cast" warning ,采用其他答案中提到的两个策略 .
在运行时将感兴趣类型的类作为参数传递(
Class<T> inputElementClazz
) . 然后你可以使用:inputElementClazz.cast(anyObject);
对于Collection的类型转换,请使用通配符?而不是泛型类型T,以确认您确实不知道遗留代码(
Collection<?> unknownTypeCollection
)期望的对象类型 . 毕竟,这就是"unchecked cast"警告要告诉我们的:我们不能确定我们得到Collection<T>
,所以诚实的做法是使用Collection<?>
. 如果绝对需要,仍然可以构建已知类型的集合(Collection<T> knownTypeCollection
) .以下示例中的遗留代码接口在StructuredViewer中具有属性“input”(StructuredViewer是树或表小部件,“input”是其背后的数据模型) . 这个“输入”可以是任何类型的Java Collection .
当然,如果我们使用具有错误数据类型的遗留代码(例如,如果我们将数组设置为StructuredViewer的“输入”而不是Java Collection),则上面的代码可能会给出运行时错误 .
调用方法的示例:
如果你发布你的代码,快速猜测可以肯定地说,但你可能已经做了一些事情
当你需要时会产生警告
它可能值得一看
Generics in the Java Programming Language
如果你不熟悉需要做什么 .
两种方式,一种是完全避免标签,另一种是使用顽皮但很好的实用方法 .
问题是预先通用的集合......
我相信经验法则是:"cast objects one thing at a time" - 这意味着当试图在泛化世界中使用原始类时,这是因为你甚至不是一个 Map !),当你想到它时你很明显无法投射它 . 如果你有一个Map <String,?> map2那么HashSet <String> keys =(HashSet <String>)map2.keySet()不会给你一个警告,尽管这是编译器的"act of faith"(因为它可能会结果成为一个树集)...但它只是一种信仰行为 .
PS反对我在第一种方式迭代"is boring"和"takes time",答案是"no pain no gain":保证泛化集合包含Map.Entry <String,String> s,没有别的 . 您必须支付此保证 . 系统地使用泛型时,这种付款很漂亮,采用编码合规的形式,而不是机器时间!
有一种想法可能会说你应该设置Eclipse的设置来进行这种未经检查的强制转换错误,而不是警告 . 在这种情况下,你将不得不使用我的第一种方式 .
如果我必须使用不支持泛型的API ..我尝试孤立这些调用包装程序尽可能少的行调用 . 然后我使用SuppressWarnings注释并同时添加类型安全转换 .
这只是个人喜欢保持尽可能整洁的东西 .
您可以创建如下所示的实用程序类,并使用它来禁止取消选中警告 .
您可以按如下方式使用它:
关于这方面的更多讨论在这里:http://cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html
几乎计算机科学中的每个问题都可以通过添加一个间接*或其他东西来解决 .
因此,引入一个非通用对象,它具有更高级别的
Map
. 没有上下文它看起来不会很有说服力,但无论如何:*除了太多的间接水平 .
当然,显而易见的答案是不进行未经检验的演员 .
如果这是绝对必要的,那么至少要尝试限制
@SuppressWarnings
注释的范围 . 根据它的Javadocs,它可以继续局部变量;这样,它甚至不会影响整个方法 .例:
无法确定
Map
是否应该具有通用参数<String, String>
. 你必须事先知道参数应该是什么(或者当你得到ClassCastException
时你会发现) . 这就是代码生成警告的原因,因为编译器不可能知道是否安全 .在你施放它之前,只需要检查它 .
对于任何人来说,接收不确定类型的对象是很常见的 . 大量遗留的“SOA”实现传递了您不应该信任的各种对象 . (恐怖!)
EDIT 更改了示例代码一次以匹配海报's updates, and following some comments I see that instanceof doesn' t与泛型很好地匹配 . 但是,更改检查以验证外部对象似乎与命令行编译器一起使用 . 修订后的例子现已发布 .
不幸的是,这里没有很好的选择 . 请记住,所有这一切的目标是保持类型安全 . 第8.2节中的“Java Generics " offers a solutions for dealing with non-genericized legacy libraries, and there is one in particular called the "空循环技术” . 基本上,进行不安全的转换,并禁止警告 . 然后循环遍历 Map ,如下所示:
如果遇到意外类型,您将获得运行时ClassCastException,但至少它会发生在问题源附近 .
这使得警告消失了......
Esko Luontola上面的答案中的Objects.Unchecked实用函数是避免程序混乱的好方法 .
如果您不希望在整个方法上使用SuppressWarnings,Java会强制您将其放在本地方法上 . 如果您需要对成员进行强制转换,则可以生成如下代码:
使用该实用程序要清晰得多,而且您正在做的事情仍然很明显:
NOTE: 我觉得重要的是补充说,有时候警告确实意味着你做错了,比如:
编译器告诉您的是,在运行时不会检查此强制转换,因此在您尝试访问通用容器中的数据之前不会引发运行时错误 .
解决方案:在Eclipse中禁用此警告 . 不要@SuppressWarnings它,只是完全禁用它 .
上面提到的一些“解决方案”是脱节的,使代码难以理解,以抑制愚蠢的警告 .
哇;我想我找到了自己问题的答案 . 我只是不确定它是否值得! :)
问题是没有检查演员表 . 所以,你必须自己检查一下 . 您不能只使用instanceof检查参数化类型,因为参数化类型信息在运行时不可用,已在编译时擦除 .
但是,您可以使用instanceof对哈希中的每个项目执行检查,这样,您就可以构造一个类型安全的新哈希 . 而且你不会引发任何警告 .
感谢mmyers和Esko Luontola,我已经参数化了我最初在这里编写的代码,因此它可以包含在某个实用程序类中并用于任何参数化的HashMap . 如果你想更好地理解它并且不熟悉泛型,我鼓励查看这个答案的编辑历史 .
这是很多工作,可能只是很少的奖励......我不确定我是否会使用它 . 我很感激有人评论是否值得 . 另外,我很欣赏改进建议:除了抛出AssertionErrors之外,我还能做些什么吗?我可以抛出更好的东西吗?我应该把它作为一个检查过的例外吗?
这个东西很难,但这是我目前的想法:
如果您的API返回Object,那么您无能为力 - 无论如何,您将盲目地投射对象 . 你让Java抛出ClassCastExceptions,或者您可以自己检查每个元素并抛出Assertions或IllegalArgumentExceptions等等,但这些运行时检查都是等效的 . 无论你在运行时做什么,都必须禁止编译时未经检查的转换 .
我只是喜欢盲目转换,让JVM为我执行运行时检查,因为我们“知道”API应该返回什么,并且通常愿意假设API有效 . 如果需要,可以在演员阵容上方的任何地方使用泛型 . 你并没有真正购买任何东西,因为你仍然有单盲,但至少你可以从那里使用泛型,所以JVM可以帮助你避免代码中其他部分的盲目转换 .
在这种特殊情况下,大概你可以看到对SetAttribute的调用并看到类型正在进入,所以在出路时盲目地将类型转换为相同并不是不道德的 . 添加引用SetAttribute的注释并完成它 .
在HTTP会话世界中,您无法真正避免强制转换,因为API是以这种方式编写的(仅接收并返回
Object
) .但是,通过一些工作,你可以轻松地避免未经检查的演员阵容 . 这意味着它将变成一个传统的演员,在发生错误的情况下在那里给出一个
ClassCastException
) . 未经检查的异常可能会在以后的任何时候转变为CCE
而不是转换点('s the reason why it'是一个单独的警告) .用专用类替换HashMap:
然后转换为该类而不是
Map<String,String>
,将在您编写代码的确切位置检查所有内容 . 后来没有意外ClassCastExceptions
.在Eclipse Preferences中,转到Java-> Compiler-> Errors / Warnings-> Generic types并检查
Ignore unavoidable generic type problems
复选框 .这满足了问题的意图,即
如果不是精神 .
这是我在覆盖
equals()
操作时处理此问题的一种方法 .这似乎适用于Java 8(甚至用
-Xlint:unchecked
编译)