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

Java嵌套泛型转换(?)?超级类型[重复]

  •  2
  • n247s  · 技术社区  · 6 年前

    Map<?, ? extends List<?>> 而不是更简单的 Map<?, List<?>> 对于以下内容 test() 方法?

    public static void main(String[] args) {
        Map<Integer, List<String>> mappy =
            new HashMap<Integer, List<String>>();
    
        test(mappy);
    }
    
    public static void test(Map<?, ? extends List<?>> m) {}
    
    // Doesn't compile
    // public static void test(Map<?, List<?>> m) {}
    

    注意下面的方法是有效的,而且这三个方法具有相同的擦除类型。

    public static <E> void test(Map<?, List<E>> m) {}
    
    0 回复  |  直到 9 年前
        1
  •  63
  •   Community    7 年前

    从根本上说, List<List<?>> List<? extends List<?>>

    事实上,一个是另一个的子类型,但首先让我们了解它们各自的含义。

    理解语义差异

    一般来说,通配符 ? 表示一些“缺失信息”。意思是 . 因为我们不知道它是什么,所以限制了我们如何使用任何引用特定类型参数的东西。

    List 而不是 Map

    • 列表<列出<&燃气轮机&燃气轮机; 具有任何类型参数的任何类型的列表

      List<List<?>> theAnyList = new ArrayList<List<?>>();
      
      // we can do this
      theAnyList.add( new ArrayList<String>() );
      theAnyList.add( new LinkedList<Integer>() );
      
      List<?> typeInfoLost = theAnyList.get(0);
      // but we are prevented from doing this
      typeInfoLost.add( new Integer(1) );
      

      我们可以把任何 在里面 theAnyList 但是这样做我们就失去了对 他们的元素 .

    • 当我们使用 ? extends ,的 持有 一些特定的子类型列表,但我们不知道它是什么了

      List<? extends List<Float>> theNotSureList =
          new ArrayList<ArrayList<Float>>();
      
      // we can still use its elements
      // because we know they store Float
      List<Float> aFloatList = theNotSureList.get(0);
      aFloatList.add( new Float(1.0f) );
      
      // but we are prevented from doing this
      theNotSureList.add( new LinkedList<Float>() );
      

      再加上任何东西都不安全了 theNotSureList ,因为我们不知道其元素的实际类型( 它最初是一个 List<LinkedList<Float>> List<Vector<Float>> ? 我们不知道。)

    • 列出<?扩展列表<&燃气轮机&燃气轮机; . 我们不知道是什么类型的 它已经在里面了,我们不知道它的元素类型 列表

      List<? extends List<?>> theReallyNotSureList;
      
      // these are fine
      theReallyNotSureList = theAnyList;
      theReallyNotSureList = theNotSureList;
      
      // but we are prevented from doing this
      theReallyNotSureList.add( new Vector<Float>() );
      // as well as this
      theReallyNotSureList.get(0).add( "a String" );
      

      我们失去了信息 二者都 关于 theReallyNotSureList 以及 元素的元素类型 它在里面。

      分配 任何类型的 清单持有清单 去吧……)

    所以要分解它:

    //   ┌ applies to the "outer" List
    //   ▼
    List<? extends List<?>>
    //                  ▲
    //                  └ applies to the "inner" List
    

    地图

    //  ┌ Map K argument
    //  │  ┌ Map V argument
    //  ▼  ▼
    Map<?, ? extends List<?>>
    //                    ▲
    //                    └ List E argument
    

    为什么? 是必要的

    "concrete" 泛型类型具有 ,也就是说, List<Dog> is not a subtype of List<Animal> 即使 class Dog extends Animal 协方差 ,也就是说, 列表<狗> 一种 List<? extends Animal> .

    // Dog is a subtype of Animal
    class Animal {}
    class Dog extends Animal {}
    
    // List<Dog> is a subtype of List<? extends Animal>
    List<? extends Animal> a = new ArrayList<Dog>();
    
    // all parameterized Lists are subtypes of List<?>
    List<?> b = a;
    

    所以把这些想法应用到一个嵌套的 列表

    • List<String> 是的子类型 List<?> List<List<String>> . 如前所示,这可以防止我们通过向 列表 .
    • 列出<?扩展列表<&燃气轮机&燃气轮机; ,因为有界通配符允许协方差。也就是说, ?延伸 允许这样一个事实 是的子类型 列出<&燃气轮机;
    • 列出<?扩展列表<&燃气轮机&燃气轮机; 实际上是一个共享超类型:

           List<? extends List<?>>
                ╱          ╲
      List<List<?>>    List<List<String>>
      

    1. Map<Integer, List<String>> 接受 列表<字符串> 作为一种价值观。
    2. Map<?, List<?>> 接受 任何 作为一种价值观。
    3. 地图<整数,列表<字符串>&燃气轮机; 是具有不同语义的不同类型。
    4. 一个不能转换为另一个,以防止我们以不安全的方式进行修改。
    5. Map<?, ? extends List<?>>

              Map<?, ? extends List<?>>
                   ╱          ╲
      Map<?, List<?>>     Map<Integer, List<String>>
      

    泛型方法的工作原理

    通过在方法上使用类型参数,我们可以断言 列表

    static <E> void test(Map<?, List<E>> m) {}
    

    这个特别声明要求 列表 他在房间里 地图 具有相同的元素类型。我们不知道那是什么类型的 ,但我们可以抽象地使用它。这使我们能够执行“盲”操作。

    例如,这种声明可能对某种积累有用:

    static <E> List<E> test(Map<?, List<E>> m) {
        List<E> result = new ArrayList<E>();
    
        for(List<E> value : m.values()) {
            result.addAll(value);
        }
    
        return result;
    }
    

    put m 密钥类型 价值观 因为我们知道他们都是 列表 同样的 元素类型

    只是为了好玩

    列表 :

    static <E> void test(Map<?, ? extends List<E>> m) {}
    

    我们可以用类似 Map<Integer, ArrayList<String>> E .

    static <K, E, L extends List<E>> void(Map<K, L> m) {
        for(K key : m.keySet()) {
            L list = m.get(key);
            for(E element : list) {
                // ...
            }
        }
    }
    

    以及里面的一切。


    另请参见

        2
  •  1
  •   Bartek Maraszek    10 年前

    这是因为泛型的子类化规则与您所期望的略有不同。特别是如果您有:

    class A{}
    class B extends A{}
    

    然后

    List<B> 不是的子类 List<A>

    有详细的解释 here here