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.

当前回答

如果我不得不使用一个不支持泛型的API ..我尝试用尽可能少的行将这些调用隔离在包装器例程中。然后我使用SuppressWarnings注释并同时添加类型安全类型强制转换。

这只是个人的喜好,想让事情尽可能的整洁。

其他回答

解决方案:在Eclipse中禁用此警告。不要@SuppressWarnings,完全禁用即可。

上面提出的几个“解决方案”都太离谱了,为了抑制愚蠢的警告而使代码不可读。

哇;我想我找到了自己问题的答案。我只是不确定这是否值得!:)

问题是演员没有检查。所以,你必须自己检查。不能只使用instanceof检查参数化类型,因为参数化类型信息在运行时不可用,在编译时已被擦除。

但是,您可以使用instanceof对散列中的每一项执行检查,这样就可以构造一个类型安全的新散列。你不会引起任何警告。

多亏了mmyers和Esko Luontola,我将最初在这里编写的代码参数化了,所以它可以被包装在某个实用程序类中,并用于任何参数化的HashMap。如果您想更好地理解它,并且对泛型不是很熟悉,我建议您查看这个答案的编辑历史。

public static <K, V> HashMap<K, V> castHash(HashMap input,
                                            Class<K> keyClass,
                                            Class<V> valueClass) {
  HashMap<K, V> output = new HashMap<K, V>();
  if (input == null)
      return output;
  for (Object key: input.keySet().toArray()) {
    if ((key == null) || (keyClass.isAssignableFrom(key.getClass()))) {
        Object value = input.get(key);
        if ((value == null) || (valueClass.isAssignableFrom(value.getClass()))) {
            K k = keyClass.cast(key);
            V v = valueClass.cast(value);
            output.put(k, v);
        } else {
            throw new AssertionError(
                "Cannot cast to HashMap<"+ keyClass.getSimpleName()
                +", "+ valueClass.getSimpleName() +">"
                +", value "+ value +" is not a "+ valueClass.getSimpleName()
            );
        }
    } else {
        throw new AssertionError(
            "Cannot cast to HashMap<"+ keyClass.getSimpleName()
            +", "+ valueClass.getSimpleName() +">"
            +", key "+ key +" is not a " + keyClass.getSimpleName()
        );
    }
  }
  return output;
}

工作量很大,报酬可能很少……我不确定我是否会用它。我很感激任何关于人们是否认为它值得的评论。此外,我很感激改进建议:除了抛出AssertionErrors之外,还有什么更好的事情我可以做吗?有更好的东西让我扔吗?我应该让它成为一个受控异常吗?

以这个为例,它比创建一个新的HashMap要快得多,如果它已经是一个HashMap,但仍然是安全的,因为每个元素都根据它的类型进行检查…

@SuppressWarnings("unchecked")
public static <K, V> HashMap<K, V> toHashMap(Object input, Class<K> key, Class<V> value) {
       assert input instanceof Map : input;

       for (Map.Entry<?, ?> e : ((HashMap<?, ?>) input).entrySet()) {
           assert key.isAssignableFrom(e.getKey().getClass()) : "Map contains invalid keys";
           assert value.isAssignableFrom(e.getValue().getClass()) : "Map contains invalid values";
       }

       if (input instanceof HashMap)
           return (HashMap<K, V>) input;
       return new HashMap<K, V>((Map<K, V>) input);
    }

如果你确定session.getAttribute()返回的类型是HashMap,那么你不能精确地将其类型转换为该类型,而只能依赖于检查泛型HashMap

HashMap<?,?> getItems(javax.servlet.http.HttpSession session) {  
    HashMap<?,?> theHash = (HashMap<?,?>)session.getAttribute("attributeKey");
    return theHash;
} 

Eclipse会突然出现警告,但是这当然会导致难以调试的运行时错误。我不只在关键操作上下文中使用这种方法。

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;
    }


}