Eclipse给我一个如下形式的警告:
类型安全:未检查从对象转换到HashMap
这是从一个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.
Two ways, one which avoids the tag completely, the other using a naughty but nice utility method.
The problem is pre-genericised Collections...
I believe the rule of thumb is: "cast objects one thing at a time" - what this means when trying to use raw classes in a genericised world is that because you don't know what is in this Map<?, ?> (and indeed the JVM might even find that it isn't even a Map!), it obvious when you think about it that you can't cast it. If you had a Map<String, ?> map2 then HashSet<String> keys = (HashSet<String>)map2.keySet() does not give you a warning, despite this being an "act of faith" for the compiler (because it might turn out to be a TreeSet)... but it is only a single act of faith.
PS to the objection that iterating as in my first way "is boring" and "takes time", the answer is "no pain no gain": a genericised collection is guaranteed to contain Map.Entry<String, String>s, and nothing else. You have to pay for this guarantee. When using generics systematically this payment, beautifully, takes the form of coding compliance, not machine time!
One school of thought might say that you should set Eclipse's settings to make such unchecked casts errors, rather than warnings. In that case you would have to use my first way.
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;
}
}
警告抑制不是解决办法。你不应该在一个语句中执行两层类型转换。
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;
}
在HTTP Session世界中,您无法真正避免强制转换,因为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>,所有内容都将在您编写代码的确切位置进行检查。以后不会出现意外的classcastexception。