类型安全性:未经检查的从Object到HashMap的强制转换我无法控制返回哪个API的API:
HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {
HashMap<String, String> theHash = (HashMap<String, String>)session.getAttribute("attributeKey");
return theHash;
}
如果可能的话,我想避免Eclipse警告,因为从理论上讲,它们至少指示可能的代码问题。不过,我还没有找到消除这种情况的好方法。我可以自己将涉及到方法的单行提取出来,然后将
@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.
#1 楼
当然,显而易见的答案是不要进行未经检查的强制转换。如果绝对必要,则至少尝试限制
@SuppressWarnings
批注的范围。根据它的Javadocs,它可以使用局部变量。这样,它甚至不会影响整个方法。示例:
@SuppressWarnings("unchecked")
Map<String, String> myMap = (Map<String, String>) deserializeMap();
无法确定
Map
是否真的应该具有通用参数<String, String>
。您必须事先知道参数应该是什么(否则您会在得到ClassCastException
时找到答案)。这就是代码生成警告的原因,因为编译器可能无法知道它是否安全。评论
+1指出它可以使用局部变量。 Eclipse仅提供将其添加到整个方法中。
– thSoft
2010年5月26日上午11:05
Eclipse 3.7(Indigo)支持将未检查的添加到局部变量。
–sweetfa
2012年2月1日下午0:11
警告不仅是因为编译器不知道强制转换是安全的。例如String s =(String)new Object();即使编译器不知道强制转换是安全的,也不会收到警告。该警告是因为编译器(a)不知道强制转换是安全的,并且(b)在强制转换时不会生成完整的运行时检查。将检查它是否为Hashmap,但不会检查它为HashMap
–西奥多·诺维(Theodore Norvell)
13年2月28日在16:46
遗憾的是,即使强制转换和警告用于赋值,注释也必须放在变量声明中...因此,如果声明和赋值位于不同的位置(例如,分别在“ try”块的内部和内部) ,Eclipse现在会生成两个警告:原始未检查的强制转换和新的“不必要的注释”诊断。
– Ti Strga
13年7月31日在16:46
需要与局部变量声明一起使用的批注的变通方法是在实际的转换范围内创建局部变量,以专门在同一行执行转换,该批注可能与实际转换在不同的行中处于不同的范围作为声明。然后将此变量分配给范围不同的实际变量。这也是我用来抑制对实例变量进行强制转换的警告的方法,因为注释也无法在此处应用。
–杰夫·洛克哈特(Jeff Lockhart)
2015年6月11日,下午5:25
#2 楼
不幸的是,这里没有很好的选择。请记住,所有这些的目的是保持类型安全。 “ Java泛型”提供了一种用于处理非泛型遗留库的解决方案,第8.2节中特别提到了一种“空循环技术”。基本上,进行不安全的投射,并抑制警告。然后像这样循环遍历地图:问题。评论
答案比skiphoppy提供的答案好得多,原因有很多:1)此代码短得多了。 2)此代码实际上会按预期引发ClassCastException。 3)此代码不会完整复制源地图。 4)循环可以很容易地包装在断言中使用的单独方法中,这很容易消除生产代码中的性能问题。
– Stijn de Witt
2014年6月6日19:28
Java编译器或JIT编译器是否有可能会确定该代码的结果未被使用并通过不对其进行“优化”?
–RenniePet
2014-09-22 23:13
如果它可能引发异常,它并不是真正的无效代码。我对目前使用的JIT编译器了解不足,无法保证它们不会弄乱这一点,但是我很自信地说它们不应该这样做。
–GrandOpener
16年5月29日在5:19
这仍然不能保证类型安全,因为仍在使用同一映射。它最初可能被定义为Map
#3 楼
哇;我想我已经找到了我自己问题的答案。我只是不确定是否值得! :)问题是未检查演员表。因此,您必须自己检查。您不能只使用instanceof检查参数化类型,因为参数化类型信息在运行时不可用,并且在编译时已被擦除。使用instanceof进行哈希处理,然后,您可以构造类型安全的新哈希。而且您不会招致任何警告。
感谢mmyers和Esko Luontola,我已经将我最初在此处编写的代码参数化,因此可以将其包装在某个实用程序类中,并用于参数化的HashMap。如果您想更好地理解它,并且对泛型不是很熟悉,我鼓励您查看此答案的编辑历史记录。几乎没有回报...我不确定是否要使用它。对于人们认为是否值得的任何评论,我将不胜感激。另外,我也很高兴提出改进建议:除了抛出AssertionErrors之外,还有其他更好的方法吗?有什么我可以扔的更好的东西吗?我应该将其设为受检查的异常吗?
评论
这些东西令人困惑,但是我认为您所做的只是将ClassCastExceptions换为AssertionErrors。
–达斯汀·盖茨(Dustin Getz)
2011年3月1日23:00
杜德,那绝对不值得!想象一下可怜的树液,他必须回来并在那里修改一些混乱的代码。我不喜欢抑制警告,但我认为这是较小的邪恶。
– Craig B
2011年12月19日,下午2:21
这不仅是一个丑陋,令人困惑的混乱(当您无法避免一个丰富的注释可以使维护程序员逐步解决时);对集合中的每个元素进行迭代将转换从O(1)转换为O(n)操作。这是无法预料的,并且很容易变成可怕的神秘减速。
–丹在火光中摆弄
2012年9月10日于20:46
@DanNeely,您是正确的。通常,没有人应该这样做。
–斯基皮舞
2012年12月7日在17:21
一些评论...方法签名是错误的,因为它不会“投射”该死的东西,它只是将现有地图复制到新地图中。而且,它可能可以重构为接受任何地图,而不依赖于HashMap本身(即,即使内部类型为HashMap,也可以使用Map并在方法签名中返回Map)。您实际上并不需要进行强制转换或将其存储到新映射中-如果您没有引发断言错误,那么到现在为止,给定的映射在其内部具有正确的类型。使用泛型类型创建新地图毫无意义,因为您仍然可以将其原始并放入任何内容。
–MetroidFan2002
2014年3月6日20:22
#4 楼
在Eclipse Preferences中,转到Java-> Compiler-> Errors / Warnings-> Generic类型并选中Ignore unavoidable generic type problems
复选框。 这满足了问题的意图,即
我想避免Eclipse警告...
如果不是精神的话。
评论
嗯,为此,谢谢:)我收到了“使用未经检查或不安全的操作”的信息。 javac中的错误,但是添加@SuppressWarnings(“ unchecked”)使Eclipse感到不满,声称该抑制是不必要的。取消选中此框可使Eclipse和javac行为相同,这正是我想要的。在代码中明确禁止警告比在Eclipse内部各处禁止警告要清晰得多。
–dimo414
15年2月8日在19:48
#5 楼
您可以创建如下所示的实用程序类,并使用它来抑制未选中的警告。 >public class Objects {
/**
* Helps to avoid using {@code @SuppressWarnings({"unchecked"})} when casting to a generic type.
*/
@SuppressWarnings({"unchecked"})
public static <T> T uncheckedCast(Object obj) {
return (T) obj;
}
}
关于此的更多讨论位于:
http://cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html
评论
而不是拒绝投票,但是包装器只是抑制警告而没有添加任何内容。
–达斯汀·盖茨(Dustin Getz)
2011年3月1日23:10
+1,因为此解决方案不会浪费宝贵的代码行。
– Tino
14年7月31日在22:31
@ErikE太多了。更大,更昂贵的更大,更高分辨率的显示器可以为所有这些浪费的线路留出空间,更大的桌子可以将所有这些更大的显示器放到桌子上,更大的房间可以将更大的桌子放进去,以及有见识的老板
– Tino
18年8月30日在14:54
@ErikE滚动条,用于vi?你在开玩笑吗?
– Tino
18年8月30日在15:01
#6 楼
这些东西很难,但是这是我目前的想法:如果您的API返回Object,那么您将无能为力-无论如何,您将盲目地铸造对象。您可以让Java抛出ClassCastExceptions,或者您可以自己检查每个元素并抛出Assertions或IllegalArgumentExceptions或类似的东西,但是这些运行时检查都是等效的。无论您在运行时执行什么操作,都必须抑制未检查的转换时间。
我只希望盲目转换,让JVM为我执行运行时检查,因为我们“知道”了什么API应该返回,并且通常愿意假定该API可以正常工作。如果需要,请在演员表上方的各处使用泛型。您实际上并没有在那里购买任何东西,因为您仍然可以进行盲目转换,但是至少您可以从那里开始使用泛型,因此JVM可以帮助您避免在代码的其他部分中盲目转换。
在这种特殊情况下,大概可以看到对SetAttribute的调用并看到类型即将传入,因此在输出时将类型盲目转换为相同类型并不是不道德的。添加引用SetAttribute的注释并完成它。
#7 楼
这是一个简化的示例,它通过采用其他答案中提到的两种策略来避免“未选中的强制转换”警告。 )。然后,您可以使用:Class<T> inputElementClazz
对于Collection的类型转换,请使用通配符?而不是通用类型T来确认您确实不知道从旧版代码(
inputElementClazz.cast(anyObject);
)中期望什么样的对象。毕竟,这就是“未经检查的强制转换”警告要告诉我们的:我们无法确定是否得到Collection<?> unknownTypeCollection
,因此诚实的做法是使用Collection<T>
。如果绝对需要,则仍可以构建已知类型的集合(Collection<?>
)。小部件,“输入”是其背后的数据模型)。此“输入”可以是任何类型的Java集合。例如,如果我们将数组设置为StructuredViewer的“输入”而不是Java集合)。调用方法的示例:
public void dragFinished(StructuredViewer structuredViewer, Class<T> inputElementClazz) {
IStructuredSelection selection = (IStructuredSelection) structuredViewer.getSelection();
// legacy code returns an Object from getFirstElement,
// the developer knows/hopes it is of type inputElementClazz, but the compiler cannot know
T firstElement = inputElementClazz.cast(selection.getFirstElement());
// legacy code returns an object from getInput, so we deal with it as a Collection<?>
Collection<?> unknownTypeCollection = (Collection<?>) structuredViewer.getInput();
// for some operations we do not even need a collection with known types
unknownTypeCollection.remove(firstElement);
// nothing prevents us from building a Collection of a known type, should we really need one
Collection<T> knownTypeCollection = new ArrayList<T>();
for (Object object : unknownTypeCollection) {
T aT = inputElementClazz.cast(object);
knownTypeCollection.add(aT);
System.out.println(aT.getClass());
}
structuredViewer.refresh();
}
#8 楼
在HTTP会话世界中,您真的不能避免强制类型转换,因为API是以这种方式编写的(仅接受并返回Object
)。 ,虽然。这意味着它将变成传统的强制类型转换,以便在发生错误时提供ClassCastException
。未经检查的异常可能会在以后的任何时候变成CCE
而不是强制转换的点(这就是它单独警告的原因)。用专用类替换HashMap:
import java.util.AbstractMap;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Attributes extends AbstractMap<String, String> {
final Map<String, String> content = new HashMap<String, String>();
@Override
public Set<Map.Entry<String, String>> entrySet() {
return content.entrySet();
}
@Override
public Set<String> keySet() {
return content.keySet();
}
@Override
public Collection<String> values() {
return content.values();
}
@Override
public String put(final String key, final String value) {
return content.put(key, value);
}
}
然后将其强制转换为该类,而不是
Map<String,String>
,所有内容将在编写代码的确切位置进行检查。以后没有意外的ClassCastExceptions
。评论
这确实是很有帮助的答案。
– GPrathap
15年11月18日在6:48
#9 楼
在Android Studio中,如果要禁用检查功能,可以使用://noinspection unchecked
Map<String, String> myMap = (Map<String, String>) deserializeMap();
#10 楼
在这种情况下,我不会将Maps直接存储到HttpSession中,而是将自己的类的实例存储到自己的类中,而该类又包含一个Map(该类的实现细节)。然后,您可以确保地图中的元素类型正确。 :public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("a", 1);
map.put("b", 2);
Object obj = map;
Map<String, Integer> ok = safeCastMap(obj, String.class, Integer.class);
Map<String, String> error = safeCastMap(obj, String.class, String.class);
}
@SuppressWarnings({"unchecked"})
public static <K, V> Map<K, V> safeCastMap(Object map, Class<K> keyType, Class<V> valueType) {
checkMap(map);
checkMapContents(keyType, valueType, (Map<?, ?>) map);
return (Map<K, V>) map;
}
private static void checkMap(Object map) {
checkType(Map.class, map);
}
private static <K, V> void checkMapContents(Class<K> keyType, Class<V> valueType, Map<?, ?> map) {
for (Map.Entry<?, ?> entry : map.entrySet()) {
checkType(keyType, entry.getKey());
checkType(valueType, entry.getValue());
}
}
private static <K> void checkType(Class<K> expectedType, Object obj) {
if (!expectedType.isInstance(obj)) {
throw new IllegalArgumentException("Expected " + expectedType + " but was " + obj.getClass() + ": " + obj);
}
}
评论
太棒了我认为我可以将其与我的答案相结合以对其进行参数化,而不必完全消除警告!
–斯基皮舞
09年2月4日,0:47
+1可能是最佳配方(易于理解和维护),可通过运行时检查安全地进行操作
– Tino
14年7月31日在22:37
#11 楼
Esko Luontola在上面的答案中的Objects.Unchecked实用程序功能是避免程序混乱的好方法。本地人。如果您需要对成员进行强制转换,则可能会导致如下代码: br />@SuppressWarnings("unchecked")
Vector<String> watchedSymbolsClone = (Vector<String>) watchedSymbols.clone();
this.watchedSymbols = watchedSymbolsClone;
注意:
我感到很重要的一点是,有时警告确实表示您在做错事,例如:
编译器告诉您的是,将在运行时不检查此强制转换,因此在尝试访问通用容器中的数据之前,不会引发运行时错误。
#12 楼
警告抑制不是解决方案。您不应该在一个语句中进行两级转换。HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {
// first, cast the returned Object to generic HashMap<?,?>
HashMap<?, ?> theHash = (HashMap<?, ?>)session.getAttribute("attributeKey");
// next, cast every entry of the HashMap to the required type <String, String>
HashMap<String, String> returingHash = new HashMap<>();
for (Entry<?, ?> entry : theHash.entrySet()) {
returingHash.put((String) entry.getKey(), (String) entry.getValue());
}
return returingHash;
}
评论
他五岁的问题?您需要做很多工作吗?给定Java具有类型擦除功能,第二个哈希图在运行时应与第一个相同。我认为这样会更有效,并且如果您只是遍历条目并验证它们都是字符串实例,则避免复制。或者,TBH,检查您正在使用的servlet JAR的来源,并验证它是否只放入了字符串。
–Rup
15年3月13日在14:35
到目前为止,我在项目中看到此警告。他的问题不是验证类型,而是由“放”入未铸造地图引起的警告。
–阿巴斯
15年3月14日在7:37
#13 楼
如果您快速发布代码,可以肯定地说一遍,但您可能已经按照HashMap<String, Object> test = new HashMap();
做了一些事情,这在需要执行操作时会发出警告
HashMap<String, Object> test = new HashMap<String, Object>();
如果您不熟悉什么需求,那么可能值得一看。有待完成。
评论
不幸的是,情况并非如此简单。代码已添加。
–斯基皮舞
09年2月3日在22:19
我来这里的目的是寻找一个稍有不同的问题的答案:您告诉了我我到底需要什么!谢谢!
–staticsan
2010-09-17在0:21
#14 楼
我可能误解了这个问题(一个示例和一些周围的线条会很好),但是为什么不总是使用适当的接口(和Java5 +)呢?我看不出您为什么要转换为HashMap
而不是Map<KeyType,ValueType>
的理由。实际上,我无法想象有任何理由将变量的类型设置为HashMap
而不是Map
。为什么源代码是
Object
?它是旧式集合的参数类型吗?如果是这样,请使用泛型并指定所需的类型。评论
我很确定在这种情况下切换到Map不会改变任何东西,但是感谢编程技巧,这可能会改变我做某些事情的方式,从而更好。对象的来源是我无法控制的API(添加了代码)。
–斯基皮舞
09年2月3日在22:19
#15 楼
如果必须使用不支持泛型的API。我尝试以尽可能少的行将这些调用隔离在包装程序中。然后,我使用SuppressWarnings批注,并同时添加类型安全强制类型转换。#16 楼
采取这个方法,它比创建一个新的HashMap要快得多(如果已经创建了一个,但仍然很安全),因为每个元素都根据其类型进行了检查。评论
key.isAssignableFrom(e.getKey()。getClass())可以写为key.isInstance(e.getKey())
–user102008
11年8月16日在0:36
#17 楼
只需在投放之前对其进行类型检查。许多传统的“ SOA”实现绕过了您不应该始终信任的各种对象。 (非常恐怖!)但是,更改检查以验证外部对象似乎可以在命令行编译器中很好地发挥作用。现在发布修改后的示例。评论
不幸的是,泛型使这不可能。它不仅仅是一个HashMap,它还是具有类型信息的HashMap。如果我消除了这些信息,我只会将警告发送到其他地方。
–斯基皮舞
09年2月3日在22:23
#18 楼
通过添加一定级别的间接*或诸如此类的东西,几乎可以解决计算机科学中的每个问题。因此引入一个比
Map
更高级的非通用对象。在没有上下文的情况下,它看起来不会很令人信服,但无论如何:评论
引述归功于已故的大卫·惠勒教授。 en.wikipedia.org/wiki/…
– Stephen C
13年19月19日下午5:56
#19 楼
这是当我重写equals()
操作时处理此问题的一种方法。#20 楼
如果您确定session.getAttribute()返回的类型是HashMap,则不能将其类型转换为该确切类型,而只能依靠检查常规HashMap。 > Eclipse随后将使警告出乎意料,但这当然会导致运行时错误,这些错误很难调试。我仅在非关键操作环境中使用此方法。#21 楼
有两种方法,一种完全避免使用标签,另一种则使用顽皮但不错的实用程序方法。一次” –在通用环境中尝试使用原始类时,这意味着因为您不知道Map ,?>中包含什么(实际上JVM甚至可能发现它不是甚至是地图!),当您想到它时就很明显无法进行投射。如果您有Map反对我以第一种方式反复进行“很无聊”和“花费时间”的反对意见, “不费吹灰之力”:保证泛型集合不包含Map.Entry
package scratchpad;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
public class YellowMouse {
// First way
Map<String, String> getHashMapStudiouslyAvoidingSuppressTag(HttpSession session) {
Map<?, ?> theHash = (Map<?, ?>)session.getAttribute("attributeKey");
Map<String, String> yellowMouse = new HashMap<String, String>();
for( Map.Entry<?, ?> entry : theHash.entrySet() ){
yellowMouse.put( (String)entry.getKey(), (String)entry.getValue() );
}
return yellowMouse;
}
// Second way
Map<String, String> getHashMapUsingNaughtyButNiceUtilityMethod(HttpSession session) {
return uncheckedCast( session.getAttribute("attributeKey") );
}
// NB this is a utility method which should be kept in your utility library. If you do that it will
// be the *only* time in your entire life that you will have to use this particular tag!!
@SuppressWarnings({ "unchecked" })
public static synchronized <T> T uncheckedCast(Object obj) {
return (T) obj;
}
}
评论
您没有评论权限的事实不允许您编辑其他人的答案以添加评论;您可以编辑其他人的答案,以改善其格式,语法等信息,而不是对它们发表意见。当您达到50个代表时,您将可以在任何地方发表评论,与此同时,我确信您可以抗拒(或者,如果您确实不能发表评论,则将评论写到帖子中的现有答案)。 (给其他人的提示:我写这封信是因为我看到了-拒绝了他对审核工具中其他帖子的建议评论-编辑)
–意大利Matteo
2011-3-25在1:03
#22 楼
这使警告消失... static Map<String, String> getItems(HttpSession session) {
HashMap<?, ?> theHash1 = (HashMap<String,String>)session.getAttribute("attributeKey");
HashMap<String,String> theHash = (HashMap<String,String>)theHash1;
return theHash;
}
评论
不,不是。实际上,这会在首先出现一个警告的情况下创建两个警告。
– Stijn de Witt
2010年11月11日在18:19
喔好吧。不知道为什么我会那样想。
– lukewm
2014年6月6日13:53
#23 楼
解决方案:在Eclipse中禁用此警告。请勿@SuppressWarnings警告,只需完全禁用它即可。评论
请问为什么?在全球范围内禁用警告会隐藏此问题所在的其他地方。添加@SuppressWarnings不会使代码根本不可读。
– MByD
13年2月28日在20:49
评论
如果您使用的是HttpSession,请查看Brian Goetz关于该主题的文章:ibm.com/developerworks/library/j-jtp09238.html如果不可避免地使用未经检查的强制类型转换,则一个好主意是将其与逻辑上表示其类型的内容紧密结合(例如枚举,甚至是Class
相关/欺骗:类型安全:未经检查的演员表
类型安全性的可能重复项:未经检查的强制转换
我要添加的是,我发现只能在包含违规代码的方法级别添加@SuppressWarnings(“ unchecked”)。因此,我将代码分解为必须执行的例程。我一直认为您可以在相关行的上方立即执行此操作。