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.

当前回答

在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。

其他回答

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


}

这很难,但以下是我目前的想法:

如果你的API返回Object,那么你什么也做不了——无论如何,你都将盲目地强制转换对象。您可以让Java抛出classcastexception,或者您可以自己检查每个元素并抛出断言或illegalargumentexception或诸如此类的东西,但这些运行时检查都是等效的。无论在运行时做什么,都必须抑制编译时未检查的强制转换。

我只是更喜欢盲强制转换,让JVM为我执行它的运行时检查,因为我们“知道”API应该返回什么,并且通常愿意假设API工作。如果需要,可以在类型转换上方的任何地方使用泛型。您实际上并没有购买任何东西,因为您仍然拥有单一的盲强制转换,但至少您可以从这里开始使用泛型,因此JVM可以帮助您避免在代码的其他部分使用盲强制转换。

在这个特殊的例子中,假设您可以看到对SetAttribute的调用,并看到进入的类型,因此在退出时将类型盲强制转换为same并不是不道德的。添加一个引用SetAttribute的注释并完成它。

我可能误解了这个问题(一个示例和几行代码就好了),但是为什么您不总是使用合适的接口(和Java5+)呢?我看不出为什么你想要强制转换到HashMap而不是Map<KeyType,ValueType>。事实上,我想不出有什么理由将变量的类型设置为HashMap而不是Map。

为什么源是一个对象?是遗留集合的参数类型吗?如果是,请使用泛型并指定所需的类型。

快速猜测一下你是否发布了你的代码,可以肯定地说,但是你可能做了一些类似的事情

HashMap<String, Object> test = new HashMap();

当你需要做的时候,哪个会产生警告

HashMap<String, Object> test = new HashMap<String, Object>();

也许值得一看

Java编程语言中的泛型

如果你不熟悉需要做什么。

您可以创建如下所示的实用程序类,并使用它来抑制未检查的警告。

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

你可以这样使用它:

import static Objects.uncheckedCast;
...

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

这里有更多关于这个问题的讨论: http://cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html