List <Double>是List <?的子类型?扩展Number>为什么?

kah*_*fan 12 java extends subtype

这就是我所知道的:

  1. Double是子类型,NumberList<Double>不是子类型List<Number>.
  2. List<Dog>不是子类型,List<Animal>因为你可以添加Cat,List<Animal>但你不能这样做List<Dog>.
  3. List<? extends Number>表示此列表可以存储Number类型的变量和Number子类型的变量.List<Double>表示此列表可以存储Double类型的变量.

如果上面的任何错误,请纠正我,然后是List<Double>子类型List<? extends Number>和为什么?

aio*_*obe 11

你的所有物品都是正确的.

  1. Double是子类型,NumberList<Double>不是子类型List<Number>.

  2. List<Dog>不是子类型,List<Animal>因为你可以添加Cat,List<Animal>但你不能这样做List<Dog>.

那是对的.泛型不是协变的(但数组是!).以下是一些后续阅读:为什么数组是协变的但是泛型是不变的?

  1. List<? extends Number>表示此列表可以存储类型的Number变量和子类型的变量Number.List<Double>表示此列表可以存储类型的变量Double.

这是事实,但是List<Number>和之间有一个重要的区别List<? extends Number>.你可以认为List<? extends Number>作为一个具体的列表Number亚型(即一List<Double>,List<Integer>,List<Long>,...)和List<Number>一种能潜在地包含的混合列表Double,Integer...

至于你的最后一个问题:

是... List<Double>的子类型List<? extends Number>

是的,你可以拥有

List<Double> doubles = new ArrayList<>();
List<? extends Number> numbers = doubles;
Run Code Online (Sandbox Code Playgroud)

......为什么?

这就是定义子类型的方式.

至于动机,假设你有一个接受数字列表的方法.如果让参数具有类型List<Number>,则无法将参数传递List<Double>给它.(您的问题中的第二项解释了原因!)相反,您可以让参数具有类型List<? extends Number>.既然List<Double>List<? extends Number>它的子类型将会解决.


Sil*_*cea 6

在运行时,List<T>与(1)List<U>相同List.

但是,这将随着值类型的引入而改变(预计将在JDK 9或JDK 10版本中发布,而不是在2016年中期之前).由于Brian Goetz在此解释了许多限制因素,因此将不再List<T>相同List<U>:http://cr.openjdk.java.net/~briangoetz/valhalla/specialization.html

(1) - TU以前的陈述中的类型不同

  • OP询问他的三个项是否正确,然后询问*是`List <Double>``List <?的子类型?扩展数字>`以及为什么?*我没有看到你在答案中如何解决这个问题.(这不是一个有不同观点的问题.) (2认同)

Fuz*_*ulz 6

还应该添加几点

  1. 在运行时List<Double>,List<? extends Number>,List<?>List<Object>都是相同的.通用参数根本不编译.所有使用泛型的魔法都是编译时的乐趣.这也意味着如果你有一个List空的,你不知道泛型参数是什么!

  2. 尽量不要将泛型参数视为"子类型",泛型参数实际上意味着" 使用泛型参数 ",因此在这种情况下" 列表使用数字 ".一个很好的例子就是HashMap源代码,如果你看看它的内部工作原理,它实际上是存储一个数组Entry,并且所有条目都存储了键和值.当你看到泛型的更复杂的用途时,你偶尔会看到这种用法.

    List泛型参数的情况下意味着列表存储该类型的对象,可能是对象永远不会存储泛型参数类型的对象!像这样:

    public class DummyIterator<O> implements Iterator<O>{
        public boolean hasNext() {
            return false;
        }
    
        public O next() {
            return null;
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 究竟List<? extends Number>是什么意思?嗯,它与List<Number>大多数用途几乎相同.请记住,虽然说?你几乎都在说"我不关心这种类型",它在这种情况下表现出来:

    List<Double> doubles = new ArrayList<Double>();
    List<? extends Number> numbers = doubles;
    numbers.add(new Double(1));  //COMPILE ERROR
    Number num = numbers.get(0);
    
    Run Code Online (Sandbox Code Playgroud)

    所以我们不能添加Double一个<? extends Number>.但是对于这个例子:

    List<Double> doubles = new ArrayList<Double>();
    List<Number> numbers = doubles; //COMPILE ERROR
    numbers.add(new Integer(1));
    Number num = numbers.get(0);
    
    Run Code Online (Sandbox Code Playgroud)

    你不能分配List<Double>List<Number>这是有道理的,你是专门告诉它,列出只使用数种

  4. 那么你应该在哪里使用??你真的可以说"我不关心通用参数",例如:

    boolean equalListSizes(List<?> list1, List<?> list2) {
      return list1.size() == list2.size();
    }
    
    Run Code Online (Sandbox Code Playgroud)

    ? extends Number只有在使用generic参数修改对象的情况下,才会使用格式类型.例如:

      Number firstItem(List<? extends Number> list1) {
        return list1.get(0);
      }
    
    Run Code Online (Sandbox Code Playgroud)
  5. 而不是使用?? extends Number格式尝试在类/方法上使用泛型,在大多数情况下,它使您的代码更具可读性!:

    <T extends Number> T firstItem(List<T> list1) {
      return list1.get(0);
    }
    
    Run Code Online (Sandbox Code Playgroud)

    类:

    class Animal{}
    class Dog extends Animal{}
    class AnimalHouse<A extends Animal> {
        List<A> animalsInside = new ArrayList<A>(); 
        void enterHouse(A animal){
            animalsInside.add(A);
        }
    
        A leaveHouse() {
            return animalsInside.remove(0);
        }
    }
    AnimalHouse<Dog> ah = new AnimalHouse<Dog>();
    ah.enterHouse(new Dog());
    Dog rufus = ah.leaveHouse();
    
    Run Code Online (Sandbox Code Playgroud)
  6. 作为泛型的奖励思想,您还可以参数化方法以返回特定类.一个很好的例子是junit中的any()方法和空列表集合:

    Dog rufus = Matchers.<Dog>any();
    List<Dog> dogs = Collections.<Dog>emptyList();
    
    Run Code Online (Sandbox Code Playgroud)

    此语法允许您指定对象的返回类型.有时知道非常有用(使一些铸造多余)!