Java嵌套泛型类型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22806202/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 18:01:31  来源:igfitidea点击:

Java nested generic type

javagenericsbounded-wildcardunbounded-wildcard

提问by Louis Bliss

How come one must use the generic type Map<?, ? extends List<?>>instead of a simpler Map<?, List<?>>for the following test()method?

对于以下方法,为什么必须使用泛型类型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) {}

Noting that the following works, and that the three methods have the same erased type anyways.

请注意以下工作,并且无论如何这三种方法具有相同的擦除类型。

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

采纳答案by Radiodef

Fundamentally, List<List<?>>and List<? extends List<?>>have distinct type arguments.

从根本上说,List<List<?>>List<? extends List<?>>具有不同的类型参数。

It's actually the case that one is a subtype of the other, but first let's learn more about what they mean individually.

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

Understanding semantic differences

理解语义差异

Generally speaking, the wildcard ?represents some "missing information". It means "there was a type argument here once, but we don't know what it is anymore". And because we don't know what it is, restrictions are imposed on how we can use anything that refers to that particular type argument.

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

For the moment, let's simplify the example by using Listinstead of Map.

目前,让我们使用List代替 来简化示例Map

  • A List<List<?>>holds any kind of List with any type argument. So i.e.:

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

    We can put any Listin theAnyList, but by doing so we have lost knowledge of their elements.

  • When we use ? extends, the Listholds some specific subtype of List, but we don't know what it is anymore. So i.e.:

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

    It's no longer safe to add anything to the theNotSureList, because we don't know the actual type of its elements. (Wasit originally a List<LinkedList<Float>>? Or a List<Vector<Float>>? We don't know.)

  • We can put these together and have a List<? extends List<?>>. We don't know what type of Listit has in it anymore, and we don't know the element type of thoseLists either. So i.e.:

    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" );
    

    We've lost information bothabout theReallyNotSureList, as well asthe element type of the Lists inside it.

    (But you may note that we can assignany kind of List holding Liststo it...)

  • AList<List<?>>持有具有任何类型参数的任何类型的 List。所以即:

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

    我们可以把任何ListtheAnyList,但这样做,我们已经失去了的知识,它们的元素

  • 当我们使用 时? extendsList包含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>() );
    

    向 中添加任何内容不再安全theNotSureList,因为我们不知道其元素的实际类型。(当时它最初是一个List<LinkedList<Float>>?还是List<Vector<Float>>?我们不知道。)

  • 我们可以把这些放在一起,然后有一个List<? extends List<?>>. 我们不再知道List它里面有什么类型,我们也不知道那些Lists的元素类型。所以即:

    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以及其中的Lists的元素类型。

    (但您可能会注意到,我们可以为它分配任何类型的List 持有 Lists...)

So to break it down:

所以要分解它:

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

The Mapworks the same way, it just has more type parameters:

Map以同样的方式,它只是有更多的类型参数:

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

Why ? extendsis necessary

为什么? extends需要

You may know that "concrete"generic types have invariance, that is, List<Dog>is not a subtype of List<Animal>even if class Dog extends Animal. Instead, the wildcard is how we have covariance, that is, List<Dog>isa subtype of List<? extends Animal>.

您可能知道“具体的”泛型类型具有不变性,也就是说,List<Dog>它不是List<Animal>if的子类型class Dog extends Animal。相反,通配符是我们如何拥有协方差,即List<Dog>的子类型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;

So applying these ideas to a nested List:

因此,将这些想法应用于嵌套List

  • List<String>is a subtype of List<?>but List<List<String>>is nota subtype of List<List<?>>. As shown before, this prevents us from compromising type safety by adding wrong elements to the List.
  • List<List<String>>isa subtype of List<? extends List<?>>, because the bounded wildcard allows covariance. That is, ? extendsallows the fact that List<String>is a subtype of List<?>to be considered.
  • List<? extends List<?>>is in fact a shared supertype:

         List<? extends List<?>>
              ╱          ╲
    List<List<?>>    List<List<String>>
    
  • List<String>是的一个亚型List<?>,但List<List<String>>就是没有一个亚型List<List<?>>。如前所述,这可以防止我们通过向List.
  • List<List<String>>的子类型List<? extends List<?>>,因为有界通配符允许协方差。也就是说,? extends允许考虑List<String>作为 的子类型的事实List<?>
  • List<? extends List<?>>实际上是一个共享超类型:

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

In review

审核中

  1. Map<Integer, List<String>>accepts onlyList<String>as a value.
  2. Map<?, List<?>>accepts anyListas a value.
  3. Map<Integer, List<String>>and Map<?, List<?>>are distinct types which have separate semantics.
  4. One cannot be converted to the other, to prevent us from doing modifications in an unsafe way.
  5. Map<?, ? extends List<?>>is a shared supertype which imposes safe restrictions:

            Map<?, ? extends List<?>>
                 ╱          ╲
    Map<?, List<?>>     Map<Integer, List<String>>
    
  1. Map<Integer, List<String>>接受List<String>作为值。
  2. Map<?, List<?>>接受anyList作为值。
  3. Map<Integer, List<String>>Map<?, List<?>>是具有不同语义的不同类型。
  4. 一个不能转换为另一个,以防止我们以不安全的方式进行修改。
  5. Map<?, ? extends List<?>>是一个共享超类型,它施加了安全限制:

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


How the generic method works

泛型方法的工作原理

By using a type parameter on the method, we can assert that Listhas some concrete type.

通过在方法上使用类型参数,我们可以断言它List具有某种具体类型。

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

This particular declaration requires that allLists in the Maphave the same element type. We don't know what that type actually is, but we can use it in an abstract manner. This allows us to perform "blind" operations.

此特定声明要求 中的所有ListsMap具有相同的元素类型。我们不知道那个类型究竟什么,但我们可以抽象地使用它。这允许我们执行“盲”操作。

For example, this kind of declaration might be useful for some kind of accumulation:

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

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

We can't call puton mbecause we don't know what its key typeis anymore. However, we can manipulate its valuesbecause we understand they are all Listwith the same element type.

我们无法调用putm因为我们不再知道它的密钥类型是什么。但是,我们可以操纵它的值,因为我们知道它们都List具有相同的元素类型

Just for kicks

只是为了踢

Another option which the question does not discuss is to have both a bounded wildcard and a generic type for the List:

问题未讨论的另一个选项是同时具有有界通配符和泛型类型List

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

We would be able to call it with something like a Map<Integer, ArrayList<String>>. This is the most permissive declaration, if we only cared about the type of E.

我们可以用类似的东西来调用它Map<Integer, ArrayList<String>>。如果我们只关心E.

We can also use bounds to nest type parameters:

我们还可以使用边界来嵌套类型参数:

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) {
            // ...
        }
    }
}

This is both permissive about what we can pass to it, as well as permissive about how we can manipulate mand everything in it.

这既允许我们可以传递给它的内容,也允许我们如何操作m以及其中的所有内容。



See also

也可以看看

回答by Bartek Maraszek

This is because the subclassing rules for generics are slightly different from what you may expect. In particular if you have:

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

class A{}
class B extends A{}

then

然后

List<B>is not a subclass of List<A>

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

It's explained in details hereand the usage of the wildcard (the "?" character) is explained here.

它的详细解释在这里和通配符(以下简称“?”字符)的使用说明在这里