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.

当前回答

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

因此,引入一个非泛型对象,它的级别高于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();
}

*除了过多的间接层次。

其他回答

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

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

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

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

例子:

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

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

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

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

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

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

下面是一个简短的示例,通过使用其他回答中提到的两种策略来避免“unchecked cast”警告。

Pass down the Class of the type of interest as a parameter at runtime (Class<T> inputElementClazz). Then you can use: inputElementClazz.cast(anyObject); For type casting of a Collection, use the wildcard ? instead of a generic type T to acknowledge that you indeed do not know what kind of objects to expect from the legacy code (Collection<?> unknownTypeCollection). After all, this is what the "unchecked cast" warning wants to tell us: We cannot be sure that we get a Collection<T>, so the honest thing to do is to use a Collection<?>. If absolutely needed, a collection of a known type can still be built (Collection<T> knownTypeCollection).

下面示例中的遗留代码接口在StructuredViewer中有一个属性“input”(StructuredViewer是一个树或表小部件,“input”是它背后的数据模型)。这个“输入”可以是任何类型的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();
}

当然,如果我们使用错误的数据类型的遗留代码(例如,如果我们将一个数组设置为StructuredViewer的“输入”而不是Java Collection),上面的代码就会给出运行时错误。

调用方法的例子:

dragFinishedStrategy.dragFinished(viewer, Product.class);

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

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

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