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.
哇;我想我找到了自己问题的答案。我只是不确定这是否值得!:)
问题是演员没有检查。所以,你必须自己检查。不能只使用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之外,还有什么更好的事情我可以做吗?有更好的东西让我扔吗?我应该让它成为一个受控异常吗?
您可以创建如下所示的实用程序类,并使用它来抑制未检查的警告。
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
下面是一个简短的示例,通过使用其他回答中提到的两种策略来避免“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);