我写了一个有<String, Object>
地图的课程。我需要它来保存任意对象,但有时我需要投射一些这些对象,所以我会做类似的事情
HashMap<String, Object> map = new HashMap<String, Object>();
Object foo = map.get("bar");
if (foo instanceof HashMap) {
((HashMap<String, Integer>) foo).put("a", 5);
}
给出警告
Stuff.java:10: warning: [unchecked] unchecked cast
found : java.lang.Object
required: java.util.HashMap<java.lang.String,java.lang.Integer>
((HashMap<String, Integer>) foo).put("a", 5);
我怀疑它与泛型的使用有关。我可以使用@SupressWarnings(“unchecked”)摆脱错误,但我想知道是否有更好的方法来做到这一点。或许我得到警告的事实意味着我应该重新考虑我正在做的事情。有什么我可以做的,或者我应该只使用@SupressWarnings?
答案 0 :(得分:4)
已编辑(基于问题澄清)
转换为HashMap<String, Integer>
(顺便说一下,使用Map
代替HashMap
可以说是一个更好的选择)是一个不同的故事。遗憾的是,由于类型擦除,在这种情况下无法避免未经检查的警告。但是,您可以将其用作非通用地图:
if (foo instanceof Map) {
((Map) foo).put("a", 5);
}
你显然必须施放“获取”并且你失去(感知)类型的安全性,但是没有未经检查的警告。
这个故事必须有更多。以下代码:
Map<String, Object> map = Maps.newHashMap(); // or new HashMap<String, Object>();
Object foo = map.get("bar");
if (foo instanceof Widget) {
((Widget) foo).spin();
}
NOT 为我生成未经检查的警告。我也无法想象为什么会这样。如果您事先知道“bar”将始终返回一个小部件,请执行以下操作:
Widget widget = (Widget) map.get("bar");
widget.spin();
也可以完美地运作。我在这里错过了什么吗?
答案 1 :(得分:3)
如果其他一切(多态实现,强制转换)不适用,您可以实现异构容器,如第3版“Effective Java”中Item 33: Consider type-safe heterogeneous containers中所述。容器的责任是确保类型安全。
public class Container{
private Map<Class<?>, Object> favorites = new HashMap<Class<?>, Object>();
public <T> void set(Class<T> klass, T thing) {
favorites.put(klass, thing);
}
public <T> T get(Class<T> klass) {
return klass.cast(favorites.get(klass));
}
}
您的示例的问题在于您使用HashMap<K,V>
作为条目类型。这不能用类文字表示为类型标记。所以你必须实现某种形式的super type token:
public abstract class TypeReference<T> {}
然后,您的客户端代码将为所需的每个类型令牌扩展TypeReference:
TypeReference<?> typeToken = new TypeReference<HashMap<String, Integer>>{};
可以在运行时访问类型信息。然后,容器实现必须对类型标记的actual type parameters of(TypeReference的子类)进行检查。
这是一个完整的解决方案,但需要做很多工作。我所知道的集合库确实不支持带有类型引用的容器。
答案 2 :(得分:1)
或者我收到警告的事实意味着我应该重新考虑我正在做的事情。
你明白了。逻辑步骤是创建Map<String, Widget>
而不是Map<String, Object>
。如果出于某种原因这不是一个选项,你可以这样做:
Widget w = Widget.class.cast(foo);
w.spin();
这不会再给出编译器警告,但这并不一定意味着你的Map
混合对象是一个好习惯。
编辑:正如ChssPly76指出的那样,这实际上应该没有生成“未经检查的强制转换”警告。我在Eclipse中测试过,它确实没有给出特别的警告。您是否可以发布SSCCE(一个main()
纯粹证明问题的课程),以便我们更好地了解正在发生的事情?
编辑2 :所以您使用的地图可能包含通用结构,例如地图。这解释了一下。好吧,除了重新设计结构之外,我没有看到任何其他选项,只能使用@SuppressWarnings("unchecked")
注释。
答案 3 :(得分:1)
如果您的Map
持有相同类型的对象(例如所有小部件),那么您可以使用Map<String,Widget>
来消除演员和警告。
但是,如果您持有任意类型的对象,则表明您有更深层次的设计问题。如果你知道对象将基于名称的类型(例如“bar”总是会给你一个Widget),那么考虑使用一个名为Widget getBar()
而不是Map
的方法的对象。
如果你不知道从地图上得到什么“bar”,你就会遇到更深层次的设计问题,应该考虑使用一些面向对象的原则来减少耦合。
答案 4 :(得分:1)
我认为潜在的问题是Object类
HashMap<String, Object> map;
如果要删除转换警告,则需要指定基类/接口。
例如,你可以做到这一点Map<String, Animal> map = new LinkedHashMap<String, Animal>();
Animal pet = map.get("pet");
pet.feed();
而不是
Map<String, Object> map = new LinkedHashMap<String, Object>();
Object pet = map.get("pet");
if (pet instance of Dog)
{
((Dog)pet).feedDog();
}
if (pet instance of Cat)
{
((Cat)pet).feedCat();
}
地图的主要用途是将类似的东西放在一起。
如果你想真正放不同的东西,那么考虑写一个新的课程。
答案 5 :(得分:0)
不确定你是如何使用这些对象的,但是有:
for(Map.Entry<String, Widget> entry = map.entrySet())
{
entry.getValue().spin();
}