Java 分配给多级通配符

Java 分配给多级通配符,java,generics,bounded-wildcard,Java,Generics,Bounded Wildcard,简单类: class Pair<K,V> { } 类对{ } 还有一些作业: Collection<Pair<String,Long>> c1 = new ArrayList<Pair<String,Long>>(); Collection<Pair<String,Long>> c2 = c1; // ok Collection<Pair<String,?>> c3 = c1; //

简单类:

class Pair<K,V> {

}
类对{
}
还有一些作业:

Collection<Pair<String,Long>> c1 = new ArrayList<Pair<String,Long>>();
Collection<Pair<String,Long>> c2 = c1; // ok
Collection<Pair<String,?>> c3 = c1; // this does not compile
Collection<? extends Pair<String,?>> c4 = c1; // ok
Collection c1=新的ArrayList();
集合c2=c1;//好啊
集合c3=c1;//这是不可编译的
集合>c4=c1;//好啊
为什么第三个项目没有编译,而第四个项目完全合法

编译器错误:

Type mismatch: cannot convert from Collection<Pair<String,Long>> to Collection<Pair<String,?>>
类型不匹配:无法从集合转换为集合

Java泛型的优点之一是在编译时进行更强的类型检查。因此,泛型中声明的任何内容都应该完全匹配。为了让它成为一个简单的答案,我将使用一些例子

要初始化数字列表,您可以这样做

List<Number> numbers = new ArrayList<Number>();
通配符也是一个例外。通配符用于接受任何参数。因此,以下陈述是有效的

List<?> numbers = new ArrayList<Number>();
Map<?, ?> unKnownMap = new HashMap<String, String>();

// OR

List<Integer> intNumbers = new ArrayList<Integer>();
List<?> numbers = intNumbers;
此外,通配符推理仅在一个级别上发生。任何嵌套都应该与通常的泛型完全匹配。所以

List<List<?>> list = new ArrayList<List<String>>(); // Compile error because nested List<String> doesn not exactly match List<?>
List<List<?>> list = new ArrayList<List<?>>(); // OK - Valid 
List<List<Integer>> intList = new ArrayList<List<Integer>>();
List<List<?>> numbers = intList; // Compile error because nested List<Integer> doesn not exactly match List<?>
列表
列表>();//好-有效
List intList=new ArrayList();
列表
这就是你的情况

Collection<Pair<String,Long>> c1 = new ArrayList<Pair<String,Long>>();
Collection<Pair<String,Long>> c2 = c1; // ok
Collection<Pair<String,?>> c3 = c1; // Compile error because nested Pair<String,?> doesn not exactly match Pair<String,Long>
Collection c1=新的ArrayList();
集合c2=c1;//好啊
集合c3=c1;//编译错误,因为嵌套对不完全匹配对
所以你可以做一些像

Collection<Pair<String,?>> c3 = new ArrayList<Pair<String,?>>(c2);
//OR
Collection<Pair<String,?>> c3 = new ArrayList<Pair<String,?>>();
c3.addAll(c2);
Collection c3=新的数组列表(c2);
//或
集合c3=新的ArrayList();
c3.addAll(c2);
案例4:当你说
列表>
时,它又是第一级。这意味着它将检查所讨论的元素是否扩展了
列表

List>List=new ArrayList();//工作原理与列表l=新ArrayList()类似;
列表>>编号=新的ArrayList();//编译错误-嵌套级别类似于List lst=new ArrayList();

所以
Collection c3=new ArrayList()类似于
对p=新对()

我将尝试使用两个简单规则解释Java泛型。这些规则足以回答您的问题,并且基本上足以记住几乎任何情况:

  • 除非
    A=B
    ,否则两种通用类型
    X
    X
    永远不可分配。也就是说,泛型在默认情况下是不变的
  • 通配符允许分配
    X
    • X
    • X>
      。也就是说,在您的情况下,
      A=Pair
      B=Pair
      。由于这些类型不相等,因此它们是不可分配的;他们违反了规则1

      问题是,为什么通配符没有帮助?答案很简单:
      规则2是不可传递的。也就是说,
      X
      不能分配给
      X
      可分配给
      B=?扩展对
      (同样是因为规则2)。因此,这是合法的

      一般方法 下面是检查任何复杂泛型类型的方法:只需使用两个规则逐级检查每个泛型类型。从最外层开始。一旦某个级别违反了规则,您就知道分配是非法的;如果所有级别都遵守规则,则该分配是合法的。让我们再次考虑你的类型:

      X = Collection<Pair<String,Long>>
      Y = Collection<Pair<String,?>>
      Z = Collection<? extends Pair<String,?>> 
      
      X=Collection
      Y=收集
      Z=收集>
      
      X可分配给Y吗

      // Outermost level:
      A = Pair<String,Long>, B = Pair<String,?>
        => B is no wildcard and A != B (Rule 1), so this is illegal!
      
      //最外层:
      A=对,B=对
      =>B不是通配符,A!=B(规则1),所以这是非法的!
      
      X可分配给Z吗

      // Outermost level:
      A = Pair<String,Long>, B = ? extends Pair<String,?>
        => We got a wildcard, so Rule 2 states this is legal if the inner level is legal
      // Inner level: (we have to check both parameters)
      A = String, B = String => Equal, Rule 1 applies, fine!
      A = Long, B = ? => B is wildcard, Rule 2 applies, fine!
      
      //最外层:
      A=配对,B=?延伸对
      =>我们得到了一个通配符,因此规则2规定,如果内部级别是合法的,则这是合法的
      //内部级别:(我们必须检查两个参数)
      A=String,B=String=>Equal,规则1适用,很好!
      A=长,B=?=>B是通配符,规则2适用,很好!
      
      记住的简单规则
      每个级别的泛型嵌套要么需要完全相同(
      A=B
      ),要么
      B
      需要在此级别中包含通配符。

      首先,让我们通过删除额外的类型参数来简化代码:

      Collection<List<Long>> c1 = new ArrayList<List<Long>>();
      Collection<List<Long>> c2 = c1; // ok
      Collection<List<?>> c3 = c1; // this does not compile
      Collection<? extends List<?>> c4 = c1; // ok
      
      现在,它
      c3=c1
      是允许的,您可以看到
      c3.add(new ArrayList())
      将破坏
      c1
      的类型安全性:

      Collection<List<Long>> c1 = new ArrayList<List<Long>>();
      Collection<List<?>> c3 = c1;
      
      c3.add(Arrays.asList("foo")); 
      
      for (List<Long> l: c1) {
          for (Long value: l) {
              // Oops, value is not a Long!
          }
      }
      
      Collection c1=新的ArrayList();
      
      Collection这里要记住的关键是嵌套的通配符不会捕获

      这意味着顶级通配符(即通配符代表一个特定的东西)的“正常”行为不适用于嵌套通配符。相反,嵌套通配符代表任何类型

      例如,以以下声明为例:

      List<?> l;
      
      这不是一个特定类型的
      列表的
      集合。这是
      列表的
      集合
      ,每个列表都是一种特定类型

      例如,您预期会发生类似的情况:

      Collection<List<?>> c = new ArrayList<List<Long>>(); // Not valid, but pretend it is
      c.add(new ArrayList<Long>()); // Valid
      c.add(new ArrayList<Integer>()); // Invalid, because c is a Collection of Lists of Long
      
      该返回哪种类型
      iterator()
      返回一个
      iterator
      ,而
      next()
      返回
      E
      ,这意味着
      c.iterator().next()
      返回一个
      列表
      。这不是您期望的
      列表。为什么呢

      因为嵌套通配符不会捕获。这就是这里的关键区别。
      列表
      中的通配符不捕获单一类型的“总体”。它为
      集合中的每个元素捕获一种类型

      因此,这是完全有效的代码:

      Collection<List<?>> odd = new ArrayList<List<?>>();
      odd.add(new ArrayList<String>());
      odd.add(new ArrayList<Long>());
      List<?> l = odd.iterator().next();
              // returns the ArrayList<String>, but because odd is parameterized with
              // List<?> we can technically end up with a list of anything
      
      现在,让我们回顾一下。
      集合
      不是由
      s的
      集合
      和单个未知类型组成的
      集合
      。它是一个
      集合
      ,由
      s组成,每个对都是一对
      字符串
      和一些未知类型,可能与集合中的另一对类型相同,也可能不同。所以这是有效的:

      // Assume an appropriate object was assigned to c3
      Pair<String, ?> p1 = new Pair<String, String>("Hello", "World");
      Pair<String, ?> p2 = new Pair<String, List<String>>("Lorem", new ArrayList<>());
      Pair<String, ?> p3 = new Pair<String, Map<String, Integer>>("Ispum", new HashMap<>());
      c3.add(p1);
      c3.add(p2);
      c3.add(p3);
      
      现在,这有点棘手。顶层捕获一个扩展
      对的特定类型。因为通配符是特定类型的超类型(例如,
      列表
      列表
      的超类型),所以
      可以捕获
      ?扩展对
      ,因为前者扩展了后者。因此,因为
      的赋值与
      兼容吗?扩展对
      ,即assig
      // The following line compiles, 
      // because `ArrayList<String>` is a `List` you can get `Object`s from
      c3.add(new ArrayList<String>()); 
      
      // The following line does not compile, 
      // because type of c4 doesn't allow you to put anything into it
      c4.add(new ArrayList<String>());
      
      Collection<List<Long>> c1 = new ArrayList<List<Long>>();
      Collection<List<?>> c3 = c1;
      
      c3.add(Arrays.asList("foo")); 
      
      for (List<Long> l: c1) {
          for (Long value: l) {
              // Oops, value is not a Long!
          }
      }
      
      List<?> l;
      
      Collection<List<?>> c;
      
      Collection<List<?>> c = new ArrayList<List<Long>>(); // Not valid, but pretend it is
      c.add(new ArrayList<Long>()); // Valid
      c.add(new ArrayList<Integer>()); // Invalid, because c is a Collection of Lists of Long
      
      List<?> l = new ArrayList<String>();
      c.add(l); // Should this compile?
      
      c.iterator().next(); // Assume there is an element to return
      
      Collection<List<?>> odd = new ArrayList<List<?>>();
      odd.add(new ArrayList<String>());
      odd.add(new ArrayList<Long>());
      List<?> l = odd.iterator().next();
              // returns the ArrayList<String>, but because odd is parameterized with
              // List<?> we can technically end up with a list of anything
      
      Collection<Pair<String,Long>> c1 = new ArrayList<Pair<String,Long>>();
      Collection<Pair<String,Long>> c2 = c1;
      
      Collection<Pair<String,Long>> c1 = new ArrayList<Pair<String,Long>>();
      Collection<Pair<String,?>> c3 = c1;
      
      // Assume an appropriate object was assigned to c3
      Pair<String, ?> p1 = new Pair<String, String>("Hello", "World");
      Pair<String, ?> p2 = new Pair<String, List<String>>("Lorem", new ArrayList<>());
      Pair<String, ?> p3 = new Pair<String, Map<String, Integer>>("Ispum", new HashMap<>());
      c3.add(p1);
      c3.add(p2);
      c3.add(p3);
      
      Collection<Pair<String,Long>> c1 = new ArrayList<Pair<String,Long>>();
      Collection<? extends Pair<String,?>> c4 = c1;