代码之家  ›  专栏  ›  技术社区  ›  swampsjohn

在Java中从对象中创建,而不会得到未经检查的警告

  •  7
  • swampsjohn  · 技术社区  · 15 年前

    我写了一个有地图的班级 <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 回复  |  直到 15 年前
        1
  •  4
  •   ChssPly76    15 年前

    编辑 (基于问题澄清)

    铸造到 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();
    }
    

    不是 为我生成未选中的警告。我也无法想象为什么会这样。如果您事先知道“bar”总是返回一个小部件,请执行以下操作:

    Widget widget = (Widget) map.get("bar");
    widget.spin();
    

    也能很好地工作。我是不是错过了什么?

        2
  •  2
  •   Thomas Jung    15 年前

    如果其他一切(多态实现、强制转换)都不适用,则可以实现 heterogeneous container (slide 32) . 这在第29项:considerType safe中描述。 异质容器 在里面 Effective Java 2 nd Edition . 容器的责任是确保类型安全。

    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<?> typeToken = new TypeReference<HashMap<String, Integer>>{};
    

    在运行时可以访问类型信息。然后,容器实现必须根据 actual type parameters of 类型标记(typereference的子类)。

    这是一个完整的解决方案,但要实现很多工作。我所知道的任何集合库都不支持具有类型引用的容器。

        3
  •  1
  •   Ross    15 年前

    如果你 Map 正在保存相同类型的对象(例如所有小部件),然后可以使用 Map<String,Widget> 消除铸造和警告。

    但是,如果您持有任意类型的对象,那么这表明您有一个更深层次的设计问题。如果您知道对象将基于名称的类型(例如,“bar”总是为您提供一个小部件),那么考虑使用一个名为 Widget getBar() 而不是 地图 .

    如果你不知道从地图上得到什么样的“条”,你会遇到一个更深层次的设计问题,应该考虑使用一些面向对象的原则来减少耦合。

        4
  •  1
  •   BalusC    15 年前

    或者我得到警告意味着我应该重新考虑我在做什么。

    你说得对。逻辑步骤是创建一个 Map<String, Widget> 而不是 Map<String, Object> . 如果出于某种原因这不是一个选择,你可以做如下的事情:

    Widget w = Widget.class.cast(foo);
    w.spin();
    

    这不再给出编译器警告,但这并不意味着 Map 混合对象是一个很好的实践。

    编辑 :正如chssply76所指出的,这实际上不应该生成“unchecked cast”警告。我在Eclipse中测试过它,它确实没有给出特别的警告。你能张贴一篇文章吗? SSCCE (A类) main() 纯粹展示问题),以便我们更好地了解发生了什么?

    编辑2 :因此,您使用的映射可能包含诸如映射之类的一般结构。这就解释了这一点。好吧,除了重新设计结构外,我没有看到任何其他的选择,除了与 @SuppressWarnings("unchecked") 注释。

        5
  •  1
  •   Titi Wangsa bin Damhore    15 年前

    我认为根本的问题是对象类

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

    地图的主要用途是把相似的东西放在一起。

    如果你真的想写不同的东西,那就考虑写一个新的类。

        6
  •  0
  •   Droo    15 年前

    不确定如何使用对象,但有:

    for(Map.Entry<String, Widget> entry = map.entrySet())
    {
         entry.getValue().spin();
    }