Java嵌套泛型类型

Java嵌套泛型类型,java,generics,bounded-wildcard,unbounded-wildcard,Java,Generics,Bounded Wildcard,Unbounded Wildcard,为什么在下面的test()方法中必须使用泛型类型Map>,而不是更简单的Map> public static void main(String[] args) { Map<Integer, List<String>> mappy = new HashMap<Integer, List<String>>(); test(mappy); } public static void test(Map<?, ? ex

为什么在下面的
test()
方法中必须使用泛型类型
Map>
,而不是更简单的
Map>

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) {}
publicstaticvoidmain(字符串[]args){
映射映射=
新的HashMap();
测试(mappy);
}
公共静态空隙试验(Map>m){}
//不编译
//公共静态空隙试验(Map>m){}
注意下面的方法是有效的,并且这三种方法具有相同的擦除类型

public static <E> void test(Map<?, List<E>> m) {}
公共静态无效测试(Map基本上,
列表>
具有不同的类型参数

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

理解语义差异 一般来说,通配符
表示一些“缺少的信息”。它的意思是“这里曾经有一个类型参数,但我们不再知道它是什么”。因为我们不知道它是什么,所以我们对如何使用引用该特定类型参数的任何内容施加了限制

现在,让我们使用
List
而不是
Map
来简化示例


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

    class A{}
    class B extends A{}
    
    然后


    List
    不是
    List的子类,通配符(“?”字符)的用法已解释。

    您得到的编译错误消息是什么?类型
    Main
    中的方法
    test(Map>)
    不适用于参数
    (Map)
    不完全是答案-但您可以使用
    Map>
    。然后您需要使用
    mapinded的方法,我可以,但是我会丢失对我在mappy中输入内容的类型检查!例如,我可以编写
    mappy.put(123,new ArrayList())
    没有错误,这是不需要的。我得到了
    列表>
    ,这对我来说很清楚。我的问题更多的是为什么在我的情况下我不能使用前者,而我可以使用后者,甚至
    列表
    。编辑:"对于通用的方法,
    ?extends
    应用于地图的
    V
    。你的地图将列表参数化为
    V
    。这意味着它只能在其中存储
    List
    。使用
    List
    的地图可以使用任何类型的列表。好的,明白了。这实际上有点合乎逻辑。先生,你是一个天才,谢谢你这么好的解释!
    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>() );
    
    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" );
    
    //   ┌ 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
    
    // 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<? extends List<?>>
              ╱          ╲
    List<List<?>>    List<List<String>>
    
            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;
    }
    
    static <E> void test(Map<?, ? extends List<E>> m) {}
    
    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) {
                // ...
            }
        }
    }
    
    class A{}
    class B extends A{}