Java 如何解决未经检查的强制转换警告?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/509076/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I address unchecked cast warnings?
提问by skiphoppy
Eclipse is giving me a warning of the following form:
Eclipse 给了我以下形式的警告:
Type safety: Unchecked cast from Object to HashMap
类型安全:从 Object 到 HashMap 的未经检查的强制转换
This is from a call to an API that I have no control over which returns Object:
这是来自对 API 的调用,我无法控制哪个返回对象:
HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {
HashMap<String, String> theHash = (HashMap<String, String>)session.getAttribute("attributeKey");
return theHash;
}
I'd like to avoid Eclipse warnings, if possible, since theoretically they indicate at least a potential code problem. I haven't found a good way to eliminate this one yet, though. I can extract the single line involved out to a method by itself and add @SuppressWarnings("unchecked")to that method, thus limiting the impact of having a block of code where I ignore warnings. Any better options? I don't want to turn these warnings off in Eclipse.
如果可能,我想避免 Eclipse 警告,因为理论上它们至少表明存在潜在的代码问题。不过,我还没有找到消除这个问题的好方法。我可以将涉及的单行单独提取到一个方法中并添加@SuppressWarnings("unchecked")到该方法中,从而限制我忽略警告的代码块的影响。还有更好的选择吗?我不想在 Eclipse 中关闭这些警告。
Before I came to the code, it was simpler, but still provoked warnings:
在我来代码之前,它更简单,但仍然引发警告:
HashMap getItems(javax.servlet.http.HttpSession session) {
HashMap theHash = (HashMap)session.getAttribute("attributeKey");
return theHash;
}
Problem was elsewhere when you tried to use the hash you'd get warnings:
当您尝试使用散列时,问题出在其他地方,您会收到警告:
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.
采纳答案by skiphoppy
Wow; I think I figured out the answer to my own question. I'm just not sure it's worth it! :)
哇; 我想我找到了自己问题的答案。我只是不确定它是否值得!:)
The problem is the cast isn't checked. So, you have to check it yourself. You can't just check a parameterized type with instanceof, because the parameterized type information is unavailable at runtime, having been erased at compile time.
问题是没有检查演员表。所以,你必须自己检查。您不能只使用 instanceof 检查参数化类型,因为参数化类型信息在运行时不可用,在编译时已被删除。
But, you can perform a check on each and every item in the hash, with instanceof, and in doing so, you can construct a new hash that is type-safe. And you won't provoke any warnings.
但是,您可以使用 instanceof 对散列中的每一项进行检查,这样您就可以构造一个类型安全的新散列。而且你不会引起任何警告。
Thanks to mmyers and Esko Luontola, I've parameterized the code I originally wrote here, so it can be wrapped up in a utility class somewhere and used for any parameterized HashMap. If you want to understand it better and aren't very familiar with generics, I encourage viewing the edit history of this answer.
感谢 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;
}
That's a lot of work, possibly for very little reward... I'm not sure if I'll use it or not. I'd appreciate any comments as to whether people think it's worth it or not. Also, I'd appreciate improvement suggestions: is there something better I can do besides throw AssertionErrors? Is there something better I could throw? Should I make it a checked Exception?
这是很多工作,可能回报很少......我不确定我是否会使用它。对于人们是否认为值得或不值得的任何评论,我将不胜感激。另外,我很感激改进建议:除了抛出 AssertionErrors 之外,我还能做些什么更好的事情吗?有什么更好的东西我可以扔吗?我应该让它成为一个检查异常吗?
回答by Mark Davidson
A quick guess if you post your code can say for sure but you might have done something along the lines of
快速猜测您是否发布了代码可以肯定地说,但您可能已经做了一些类似的事情
HashMap<String, Object> test = new HashMap();
which will produce the warning when you need to do
当你需要做的时候会产生警告
HashMap<String, Object> test = new HashMap<String, Object>();
it might be worth looking at
可能值得一看
Generics in the Java Programming Language
if your unfamiliar with what needs to be done.
如果您不熟悉需要做什么。
回答by phihag
I may have misunderstood the question(an example and a couple of surrounding lines would be nice), but why don't you always use an appropriate interface (and Java5+)? I see no reason why you would ever want to cast to a HashMapinstead of a Map<KeyType,ValueType>. In fact, I can't imagine anyreason to set the type of a variable to HashMapinstead of Map.
我可能误解了这个问题(一个例子和几行周围的行会很好),但你为什么不总是使用适当的界面(和 Java5+)?我看不出你为什么要转换为 aHashMap而不是 a Map<KeyType,ValueType>。事实上,我无法想象有任何理由将变量的类型设置为HashMap而不是Map.
And why is the source an Object? Is it a parameter type of a legacy collection? If so, use generics and specify the type you want.
为什么来源是Object?它是遗留集合的参数类型吗?如果是这样,请使用泛型并指定所需的类型。
回答by Michael Myers
The obvious answer, of course, is not to do the unchecked cast.
当然,显而易见的答案是不要进行未经检查的演员表。
If it's absolutely necessary, then at least try to limit the scope of the @SuppressWarningsannotation. According to its Javadocs, it can go on local variables; this way, it doesn't even affect the entire method.
如果绝对有必要,那么至少尝试限制@SuppressWarnings注释的范围。根据它的Javadocs,它可以继续使用局部变量;这样,它甚至不会影响整个方法。
Example:
例子:
@SuppressWarnings("unchecked")
Map<String, String> myMap = (Map<String, String>) deserializeMap();
There is no way to determine whether the Mapreally should have the generic parameters <String, String>. You must know beforehand what the parameters should be (or you'll find out when you get a ClassCastException). This is why the code generates a warning, because the compiler can't possibly know whether is safe.
没有办法确定是否Map真的应该有泛型参数<String, String>。您必须事先知道参数应该是什么(否则您会在获得 时发现ClassCastException)。这就是代码生成警告的原因,因为编译器不可能知道是否安全。
回答by Rick
Just typecheck it before you cast it.
只需在投射之前打字检查即可。
Object someObject = session.getAttribute("attributeKey");
if(someObject instanceof HashMap)
HashMap<String, String> theHash = (HashMap<String, String>)someObject;
And for anyone asking, it's quite common to receive objects where you aren't sure of the type. Plenty of legacy "SOA" implementations pass around various objects that you shouldn't always trust. (The horrors!)
对于任何询问的人来说,收到不确定类型的对象是很常见的。许多遗留的“SOA”实现会传递您不应该总是信任的各种对象。(恐怖!)
EDITChanged the example code once to match the poster's updates, and following some comments I see that instanceof doesn't play nicely with generics. However changing the check to validate the outer object seems to play well with the commandline compiler. Revised example now posted.
编辑更改了一次示例代码以匹配海报的更新,并根据一些评论我看到 instanceof 不能很好地与泛型一起使用。然而,更改检查以验证外部对象似乎与命令行编译器配合得很好。修订后的示例现已发布。
回答by Julien Chastang
Unfortunately, there are no great options here. Remember, the goal of all of this is to preserve type safety. "Java Generics" offers a solution for dealing with non-genericized legacy libraries, and there is one in particular called the "empty loop technique" in section 8.2. Basically, make the unsafe cast, and suppress the warning. Then loop through the map like this:
不幸的是,这里没有很好的选择。请记住,所有这些的目标是保持类型安全。“ Java 泛型”提供了一种处理非泛型遗留库的解决方案,在 8.2 节中有一种特别称为“空循环技术”的方法。基本上,进行不安全的转换,并抑制警告。然后像这样循环遍历地图:
@SuppressWarnings("unchecked")
Map<String, Number> map = getMap();
for (String s : map.keySet());
for (Number n : map.values());
If an unexpected type is encountered, you will get a runtime ClassCastException, but at least it will happen close to the source of the problem.
如果遇到意外类型,您将获得一个 runtime ClassCastException,但至少它会发生在靠近问题根源的地方。
回答by Fortyrunner
If I have to use an API that doesn't support Generics.. I try and isolate those calls in wrapper routines with as few lines as possible. I then use the SuppressWarnings annotation and also add the type-safety casts at the same time.
如果我必须使用不支持泛型的 API.. 我尝试用尽可能少的行隔离包装例程中的这些调用。然后我使用 SuppressWarnings 注释并同时添加类型安全强制转换。
This is just a personal preference to keep things as neat as possible.
这只是个人喜好,以保持尽可能整洁。
回答by Esko Luontola
You can create a utility class like the following, and use it to suppress the unchecked warning.
您可以创建如下所示的实用程序类,并使用它来取消未选中的警告。
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;
}
}
You can use it as follows:
您可以按如下方式使用它:
import static Objects.uncheckedCast;
...
HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {
return uncheckedCast(session.getAttribute("attributeKey"));
}
Some more discussion about this is here: http://cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html
关于这个的更多讨论在这里:http: //cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html
回答by Esko Luontola
In this particular case, I would not store Maps into the HttpSession directly, but instead an instance of my own class, which in turn contains a Map (an implementation detail of the class). Then you can be sure that the elements in the map are of the right type.
在这种特殊情况下,我不会直接将 Map 存储到 HttpSession 中,而是将我自己的类的一个实例存储到其中,该实例又包含一个 Map(类的实现细节)。然后您可以确定地图中的元素是正确的类型。
But if you anyways want to check that the contents of the Map are of right type, you could use a code like this:
但是,如果您无论如何想要检查 Map 的内容是否为正确类型,您可以使用这样的代码:
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("a", 1);
map.put("b", 2);
Object obj = map;
Map<String, Integer> ok = safeCastMap(obj, String.class, Integer.class);
Map<String, String> error = safeCastMap(obj, String.class, String.class);
}
@SuppressWarnings({"unchecked"})
public static <K, V> Map<K, V> safeCastMap(Object map, Class<K> keyType, Class<V> valueType) {
checkMap(map);
checkMapContents(keyType, valueType, (Map<?, ?>) map);
return (Map<K, V>) map;
}
private static void checkMap(Object map) {
checkType(Map.class, map);
}
private static <K, V> void checkMapContents(Class<K> keyType, Class<V> valueType, Map<?, ?> map) {
for (Map.Entry<?, ?> entry : map.entrySet()) {
checkType(keyType, entry.getKey());
checkType(valueType, entry.getValue());
}
}
private static <K> void checkType(Class<K> expectedType, Object obj) {
if (!expectedType.isInstance(obj)) {
throw new IllegalArgumentException("Expected " + expectedType + " but was " + obj.getClass() + ": " + obj);
}
}
回答by Tom Hawtin - tackline
Almost every problem in Computer Science can be solved by adding a level of indirection*, or something.
几乎计算机科学中的每个问题都可以通过添加一个间接级别* 或其他东西来解决。
So introduce a non-generic object that is of a higher-level that a Map. With no context it isn't going to look very convincing, but anyway:
因此,引入一个比 a 更高级别的非泛型对象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();
}
*Except too many levels of indirection.
*除了太多的间接级别。

