【问题标题】:another java generic question另一个java通用问题
【发布时间】:2011-06-28 09:15:44
【问题描述】:

我有以下课程:

interface Able{/* ... */}
class A implements Able{/* ... */}

我有

Map<String,? extends Able> as;
as = new HashMap<String, A>();

为什么会出现以下错误:

as.put("a", new A());

有什么想法吗?

【问题讨论】:

    标签: java generics


    【解决方案1】:

    对java泛型的引用很好(jdk site)。

    确实@Oli_Charlesworth 给出了一个很好的答案,但也许这个会更完整。

    Collection&lt;? extends Able&gt; 中不能插入任何正确的内容。

    如果你有

    class A implements Able {...}
    

    class B implement Able {...}
    

    那么,Collection&lt;? extends Able&gt; 是两者的超类型:

    Collection<A>
    Collection<B>
    

    因此写一些像

    这样的语句是合法的
    //Code snippet 01
    Collection< ? extends Able > list;
    Collection<A> listA;
    Collection<B> listB;
    list = listA;
    list = listB;
    

    这确实是通配符 Collection&lt;? extends Able&gt; 存在的原因。

    但是,这里的事情变得越来越有趣了:

    Collection&lt;A&gt; 中,您只能插入A 的对象(包括子类)。 Collection&lt;B&gt; 也一样。在这两者中,您都不能添加只是Able 的东西。例如:

    //Code snippet 02
    listA.add( new A() );  //valid at compile-time
    listA.add( new B() );  //not valid at compile-time
    listB.add( new B() );  //valid at compile-time
    listB.add( new A() );  //not valid at compile-time
    

    因此,如果您对我们在code snippets 01 &amp; 02 中看到的内容进行分组,您就会明白编译器绝对不可能接受如下语句:

    Collection< ? extends Able > list;
    list.add( new A() );         //not allowed, will work only if list is List<A>
    list.add( new B() );         //not allowed, will work only if list is List<B>
    

    所以是的,超级类型Collection&lt; ? extends Able &gt; 不接受添加任何内容。更通用的类型提供了子类型功能的交集,因此,子类型的功能更少。在这里,我们失去了添加A 对象和B 对象的能力。这些功能将在层次结构中稍后发生......甚至意味着我们不能在超类中添加任何东西Collection&lt; ? extends Able &gt;

    补充说明:

    另外,请注意,在 Collection&lt;Able&gt; 中,您可以像这样添加任何您想要的内容:

    Collection< Able > list;
    list.add( new A() );         //valid
    list.add( new B() );         //valid
    

    但是,Collection&lt;Able&gt; 不是Collection&lt;A&gt;Collection&lt;B&gt; 的超类。这意味着,与任何继承关系一样,子类可以做它们的超类可以做的任何事情,因为继承是专门化的。因此,这意味着我们可以将 A 对象和 B 对象添加到子类 Collection&lt;A&gt;Collection&lt;B&gt; 中,但事实并非如此。所以它不是你不能拥有的超类:

    Collection<Able> list;
    Collection<A> listA;
    Collection<B> listB;
    list = listA;  //not valid because there is no inheritance hierarchy
    list = listB;  //not valid because there is no inheritance hierarchy
    

    请注意,继承是一种超音序关系(泛化/专业化),而集合定义了一种美音关系(容器/容器)。将它们正式结合起来是一件令人头疼的事情,尽管人类的模糊生物在某种程度上很容易使用它,例如在法语修辞格中:synecdocque。 :)

    【讨论】:

    【解决方案2】:

    来自http://download.oracle.com/javase/tutorial/extra/generics/wildcards.html

    像往常一样,要付出代价 为了使用的灵活性 通配符。这个价格就是 现在写入 [基于通配符的容器] 是非法的。例如, 这是不允许的:

    public void addRectangle(List<? extends Shape> shapes) {
        shapes.add(0, new Rectangle()); // Compile-time error!
    }
    

    您应该能够找出原因 上面的代码是不允许的。类型 的第二个参数 shapes.add()? extends Shape-- 一个 Shape 的未知子类型。因为我们 不知道它是什么类型,我们不知道 知道它是否是一个超类型 Rectangle;它可能是也可能不是 这样的超类型,所以它是不安全的 在那里传递Rectangle

    【讨论】:

      【解决方案3】:

      理解这个问题的一个好方法是阅读通配符的含义:

      Map<String,? extends Able> as;
      

      “具有 String 类型键和 一种扩展 Able 的类型 值的映射。”

      之所以不允许添加操作,是因为它们“开门”在集合中引入不同的类型,这会与类型系统冲突。 例如

      class UnAble implements Able;
      Map<String,UnAble> unableMap = new HashMap<String,UnAble>();
      Map<String,? extends Able> ableMap = unableMap;
      ableMap.put("wontwork",new A()); // type mismatch: insert an A-type into an Unable map 
      

      通配符结构的正确用法是:

      Result processAble(Map<String,? extends Able>) { ... read records & do something ... } 
      
      Map<String,A> ableMap = new HashMap<String,A>;
      ableMap.put("willwork",new A());
      processAble(as);
      processAble(unableMap); // from the definition above
      

      【讨论】:

        【解决方案4】:

        声明

        Map<String,? extends Able> as; 
        

        表示“任何映射,具有字符串键,值是 Able 的子类型”。因此,例如,您可以执行以下操作:

        Map<String,? extends Able> as = new HashMap<String, SubSubAble>();
        

        现在让我们看看这个例子:

        Map<String,? extends Able> as = new HashMap<String, SubSubAble>();
        as.put("key", new A() );
        

        如果它是正确的,您将完成具有内容 {"key", new A()} 的 HashMap - 这是类型错误!

        【讨论】:

          【解决方案5】:

          Collection&lt;?&gt;各种集合的超类型。它不是可以容纳任何类型一个集合。至少那是我对整个概念的误解。

          我们可以在不关心泛型类型的地方使用它,就像这个例子:

          public static void print(Collection<?> aCollection) {
            for (Object o:aCollection) {
              System.out.println(o);
            }
          }
          

          如果我们选择了签名:

          public static void print(Collection<Object> aCollection)
          

          我们会将自己限制为 Collection&lt;Object&gt; 类型的集合 - 换句话说,这样的方法不会接受 Collection&lt;String&gt; 类型的值。

          所以Collection&lt;?&gt; 类型不是可以采用任何类型的集合。它只需要未知类型。而且由于我们不知道那个类型(它的未知;)),我们永远不能添加一个值,因为在 java 中没有一个类型是未知类型的子类。

          如果我们添加边界(如&lt;? extends Able&gt;),则类型仍然未知。

          您正在寻找地图的声明,其值都实现了Able 接口。正确的声明很简单:

          Map<String, Able> map;
          

          假设我们有两种类型 AB 子类 Able 和另外两个映射

          Map<String, A> aMap;
          Map<String, B> bMap;
          

          并且想要一个返回任何映射的方法,其值实现了Able 接口:那么我们使用通配符:

          public Map<String, ? extends Able> createAorBMap(boolean flag) {
           return flag ? aMap: bMap;
          }
          

          (同样有约束,我们不能将新的键/值对添加到此方法返回的映射中)。

          【讨论】:

            【解决方案6】:

            您不能在使用通配符“?”声明的集合中插入任何类型的对象。

            你只能插入“null”

            一旦将集合声明为 List,编译器就无法知道添加 SubAble 是否安全。

            如果 Collection&lt;SubSubAble&gt; 已分配给 Collection&lt;Able&gt; 会怎样?这将是一个有效的分配,但添加一个 SubAble 会污染集合。

            How can elements be added to a wildcard generic collection?

            【讨论】:

            • "如果一个 Collection 被分配给 SubAble 怎么办?"这是什么意思?
            • 对不起,需要将它们标记为代码段,否则不会显示泛型部分
            猜你喜欢
            • 2011-01-17
            • 1970-01-01
            • 1970-01-01
            • 2019-11-01
            • 1970-01-01
            • 2011-05-05
            • 2013-05-20
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多