Java 类型安全:未经检查的强制转换
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/262367/
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
Type safety: Unchecked cast
提问by DragonBorn
In my spring application context file, I have something like:
在我的 spring 应用程序上下文文件中,我有类似的内容:
<util:map id="someMap" map-class="java.util.HashMap" key-type="java.lang.String" value-type="java.lang.String">
<entry key="some_key" value="some value" />
<entry key="some_key_2" value="some value" />
</util:map>
In java class, the implementation looks like:
在java类中,实现如下:
private Map<String, String> someMap = new HashMap<String, String>();
someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");
In Eclipse, I see a warning that says:
在 Eclipse 中,我看到一条警告说:
Type safety: Unchecked cast from Object to HashMap
类型安全:从 Object 到 HashMap 的未经检查的强制转换
What did I do wrong? How do I resolve the issue?
我做错了什么?如何解决问题?
采纳答案by MetroidFan2002
Well, first of all, you're wasting memory with the new HashMap
creation call. Your second line completely disregards the reference to this created hashmap, making it then available to the garbage collector. So, don't do that, use:
好吧,首先,您在HashMap
调用新创建时浪费了内存。你的第二行完全忽略了对这个创建的哈希图的引用,使其可供垃圾收集器使用。所以,不要这样做,使用:
private Map<String, String> someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");
Secondly, the compiler is complaining that you cast the object to a HashMap
without checking if it is a HashMap
. But, even if you were to do:
其次,编译器抱怨您将对象强制转换为 aHashMap
而不检查它是否为HashMap
. 但是,即使您要执行以下操作:
if(getApplicationContext().getBean("someMap") instanceof HashMap) {
private Map<String, String> someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");
}
You would probably still get this warning. The problem is, getBean
returns Object
, so it is unknown what the type is. Converting it to HashMap
directly would not cause the problem with the second case (and perhaps there would not be a warning in the first case, I'm not sure how pedantic the Java compiler is with warnings for Java 5). However, you are converting it to a HashMap<String, String>
.
您可能仍会收到此警告。问题是,getBean
返回Object
,所以不知道类型是什么。将它HashMap
直接转换为不会导致第二种情况的问题(也许在第一种情况下不会有警告,我不确定 Java 编译器对 Java 5 的警告有多迂腐)。但是,您正在将其转换为HashMap<String, String>
.
HashMaps are really maps that take an object as a key and have an object as a value, HashMap<Object, Object>
if you will. Thus, there is no guarantee that when you get your bean that it can be represented as a HashMap<String, String>
because you could have HashMap<Date, Calendar>
because the non-generic representation that is returned can have any objects.
HashMaps 实际上是一种以对象为键并以对象为值的映射,HashMap<Object, Object>
如果您愿意的话。因此,无法保证当您获得 bean 时,它可以表示为 a,HashMap<String, String>
因为您可以拥有,HashMap<Date, Calendar>
因为返回的非泛型表示可以包含任何对象。
If the code compiles, and you can execute String value = map.get("thisString");
without any errors, don't worry about this warning. But if the map isn't completely of string keys to string values, you will get a ClassCastException
at runtime, because the generics cannot block this from happening in this case.
如果代码可以编译,并且您可以执行String value = map.get("thisString");
而没有任何错误,请不要担心此警告。但是如果映射不完全是字符串键到字符串值,您将ClassCastException
在运行时得到 a ,因为在这种情况下泛型无法阻止这种情况的发生。
回答by David M. Karr
A warning is just that. A warning. Sometimes warnings are irrelevant, sometimes they're not. They're used to call your attention to something that the compiler thinks could be a problem, but may not be.
警告就是这样。一个警告。有时警告无关紧要,有时则不然。它们用于提醒您注意编译器认为可能有问题但可能不是问题的某些内容。
In the case of casts, it's always going to give a warning in this case. If you are absolutely certain that a particular cast will be safe, then you should consider adding an annotation like this (I'm not sure of the syntax) just before the line:
在强制转换的情况下,它总是会在这种情况下发出警告。如果您绝对确定某个特定的转换是安全的,那么您应该考虑在该行之前添加这样的注释(我不确定语法):
@SuppressWarnings (value="unchecked")
回答by David Nehme
You are getting this message because getBean returns an Object reference and you are casting it to the correct type. Java 1.5 gives you a warning. That's the nature of using Java 1.5 or better with code that works like this. Spring has the typesafe version
您收到此消息是因为 getBean 返回一个 Object 引用,并且您正在将其转换为正确的类型。Java 1.5 给你一个警告。这就是将 Java 1.5 或更高版本与这样工作的代码一起使用的性质。Spring 有类型安全版本
someMap=getApplicationContext().getBean<HashMap<String, String>>("someMap");
on its todo list.
在它的待办事项清单上。
回答by Jon Skeet
The problem is that a cast is a runtime check - but due to type erasure, at runtime there's actually no difference between a HashMap<String,String>
and HashMap<Foo,Bar>
for any other Foo
and Bar
.
问题在于强制转换是运行时检查 - 但由于类型擦除,在运行时HashMap<String,String>
,HashMap<Foo,Bar>
对于任何其他Foo
和Bar
.
Use @SuppressWarnings("unchecked")
and hold your nose. Oh, and campaign for reified generics in Java :)
使用@SuppressWarnings("unchecked")
并握住您的鼻子。哦,还有在 Java 中进行具体化泛型的活动:)
回答by Larry Landry
As the messages above indicate, the List cannot be differentiated between a List<Object>
and a List<String>
or List<Integer>
.
正如上面的消息所示,列表不能区分 aList<Object>
和 aList<String>
或List<Integer>
。
I've solved this error message for a similar problem:
我已经解决了类似问题的此错误消息:
List<String> strList = (List<String>) someFunction();
String s = strList.get(0);
with the following:
具有以下内容:
List<?> strList = (List<?>) someFunction();
String s = (String) strList.get(0);
Explanation: The first type conversion verifies that the object is a List without caring about the types held within (since we cannot verify the internal types at the List level). The second conversion is now required because the compiler only knows the List contains some sort of objects. This verifies the type of each object in the List as it is accessed.
说明:第一个类型转换验证对象是一个 List 而不关心其中包含的类型(因为我们无法在 List 级别验证内部类型)。现在需要进行第二次转换,因为编译器只知道 List 包含某种对象。这会在访问列表时验证列表中每个对象的类型。
回答by Rabbit
If you really want to get rid of the warnings, one thing you can do is create a class that extends from the generic class.
如果你真的想摆脱警告,你可以做的一件事是创建一个从泛型类扩展的类。
For example, if you're trying to use
例如,如果您尝试使用
private Map<String, String> someMap = new HashMap<String, String>();
You can create a new class like such
你可以像这样创建一个新类
public class StringMap extends HashMap<String, String>()
{
// Override constructors
}
Then when you use
那么当你使用
someMap = (StringMap) getApplicationContext().getBean("someMap");
The compiler DOES know what the (no longer generic) types are, and there will be no warning. This may not always be the perfect solution, some might argue this kind of defeats the purpose of generic classes, but you're still re-using all of the same code from the generic class, you're just declaring at compile time what type you want to use.
编译器确实知道(不再是通用的)类型是什么,并且不会有警告。这可能并不总是完美的解决方案,有些人可能会争辩说这种违背了泛型类的目的,但您仍然在重新使用泛型类中的所有相同代码,您只是在编译时声明什么类型你想用。
回答by Jeremy
Another solution, if you find yourself casting the same object a lot and you don't want to litter your code with @SupressWarnings("unchecked")
, would be to create a method with the annotation. This way you're centralizing the cast, and hopefully reducing the possibility for error.
另一种解决方案是,如果您发现自己经常投射相同的对象,并且不想用 乱扔代码@SupressWarnings("unchecked")
,则可以创建一个带有注释的方法。通过这种方式,您可以集中演员阵容,并希望减少出错的可能性。
@SuppressWarnings("unchecked")
public static List<String> getFooStrings(Map<String, List<String>> ctx) {
return (List<String>) ctx.get("foos");
}
回答by Andy
Below code causes Type safety Warning
下面的代码导致类型安全警告
Map<String, Object> myInput = (Map<String, Object>) myRequest.get();
Map<String, Object> myInput = (Map<String, Object>) myRequest.get();
Workaround
解决方法
Create a new Map Object without mentioning the parameters because the type of object held within the list is not verified.
创建一个新的地图对象而不提及参数,因为列表中保存的对象类型未经验证。
Step 1:Create a new temporary Map
第 1 步:创建一个新的临时 Map
Map<?, ?> tempMap = (Map<?, ?>) myRequest.get();
Map<?, ?> tempMap = (Map<?, ?>) myRequest.get();
Step 2:Instantiate the main Map
第 2 步:实例化主 Map
Map<String, Object> myInput=new HashMap<>(myInputObj.size());
Step 3:Iterate the temporary Map and set the values into the main Map
第 3 步:迭代临时 Map 并将值设置到主 Map
for(Map.Entry<?, ?> entry :myInputObj.entrySet()){
myInput.put((String)entry.getKey(),entry.getValue());
}
回答by ochakov
The solution to avoid the unchecked warning:
避免未检查警告的解决方案:
class MyMap extends HashMap<String, String> {};
someMap = (MyMap)getApplicationContext().getBean("someMap");
回答by davidxxx
What did I do wrong? How do I resolve the issue?
我做错了什么?如何解决问题?
Here :
这里 :
Map<String,String> someMap = (Map<String,String>)getApplicationContext().getBean("someMap");
Map<String,String> someMap = (Map<String,String>)getApplicationContext().getBean("someMap");
You use a legacy method that we generally don't want to use since that returns Object
:
您使用我们通常不想使用的遗留方法,因为它返回Object
:
Object getBean(String name) throws BeansException;
The method to favor to get (for singleton) / create (for prototype) a bean from a bean factory is :
有利于从 bean 工厂获取(对于单例)/创建(对于原型)一个 bean 的方法是:
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
Using it such as :
使用它,例如:
Map<String,String> someMap = app.getBean(Map.class,"someMap");
will compile but still with a unchecked conversion warning since all Map
objects are not necessarily Map<String, String>
objects.
将编译但仍然带有未经检查的转换警告,因为所有Map
对象都不一定是Map<String, String>
对象。
But <T> T getBean(String name, Class<T> requiredType) throws BeansException;
is not enough in bean generic classes such as generic collections since that requires to specify more than one class as parameter : the collection type and its generic type(s).
但<T> T getBean(String name, Class<T> requiredType) throws BeansException;
在 bean 泛型类(例如泛型集合)中是不够的,因为这需要指定多个类作为参数:集合类型及其泛型类型。
In this kind of scenario and in general, a better approach is not to use directly BeanFactory
methods but let the framework to inject the bean.
在这种情况下,一般来说,更好的方法不是直接使用BeanFactory
方法,而是让框架注入 bean。
The bean declaration :
bean 声明:
@Configuration
public class MyConfiguration{
@Bean
public Map<String, String> someMap() {
Map<String, String> someMap = new HashMap();
someMap.put("some_key", "some value");
someMap.put("some_key_2", "some value");
return someMap;
}
}
The bean injection :
bean注入:
@Autowired
@Qualifier("someMap")
Map<String, String> someMap;