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。

其他回答

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

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

计算机科学中的几乎每一个问题都可以通过添加一层间接方法来解决。

因此,引入一个非泛型对象,它的级别高于Map。如果没有上下文,它看起来不会很有说服力,但无论如何:

public final class Items implements java.io.Serializable {
    private static final long serialVersionUID = 1L;
    private Map<String,String> map;
    public Items(Map<String,String> map) {
        this.map = New.immutableMap(map);
    }
    public Map<String,String> getMap() {
        return map;
    }
    @Override public String toString() {
        return map.toString();
    }
}

public final class New {
    public static <K,V> Map<K,V> immutableMap(
        Map<? extends K, ? extends V> original
    ) {
        // ... optimise as you wish...
        return Collections.unmodifiableMap(
            new HashMap<String,String>(original)
        );
    }
}

static Map<String, String> getItems(HttpSession session) {
    Items items = (Items)
        session.getAttribute("attributeKey");
    return items.getMap();
}

*除了过多的间接层次。

在Android Studio中,如果你想禁用检查,你可以使用:

//noinspection unchecked
Map<String, String> myMap = (Map<String, String>) deserializeMap();

当然,最明显的答案是不要进行未经检查的强制转换。

如果绝对有必要,那么至少尝试限制@SuppressWarnings注释的范围。根据它的Javadocs,它可以访问局部变量;这样,它甚至不会影响整个方法。

例子:

@SuppressWarnings("unchecked")
Map<String, String> myMap = (Map<String, String>) deserializeMap();

没有办法确定Map是否真的应该有通用参数<String, String>。您必须事先知道参数应该是什么(否则当您得到ClassCastException时就会知道)。这就是代码生成警告的原因,因为编译器不可能知道是否安全。

在强制转换之前检查一下。

Object someObject = session.getAttribute("attributeKey");
if(someObject instanceof HashMap)
HashMap<String, String> theHash = (HashMap<String, String>)someObject;  

对于任何提问的人来说,收到你不确定类型的对象是很常见的。大量遗留的“SOA”实现传递各种对象,您不应该总是信任这些对象。(恐怖!)

EDIT修改了示例代码一次,以匹配海报的更新,在一些评论之后,我看到instanceof不能很好地使用泛型。然而,更改检查以验证外部对象似乎可以很好地使用命令行编译器。修订的例子现在发布。