在Java中从Object转换而不获取未经检查的警告

时间:2009-11-21 02:58:53

标签: java generics casting

我写了一个有<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?

6 个答案:

答案 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();
}