List <double>是List <! - 的子类型吗?扩展数字 - >为什么?

时间:2015-05-28 05:50:06

标签: java extends subtype

以下是我所知道的:

  1. DoubleNumber的子类型,List<Double>不是List<Number>的子类型。
  2. List<Dog>不是List<Animal>的子类型,因为您可以将Cat添加到List<Animal>,但不能使用List<Dog>执行此操作。
  3. List<? extends Number>表示此列表可以存储Number类型的变量和Number的子类型的变量。 List<Double>表示此列表可以存储Double类型的变量。
  4. 如果上述内容有误,请更正我,然后List<Double>是[{1}}的子类型,为什么?

4 个答案:

答案 0 :(得分:11)

您的所有商品都是正确的。

  
      
  1. .col-half:nth-child(even) .thumb:after{ //some codes } .col-half:nth-child(odd) .thumb:after{ //some codes } Double的子类型,Number不是List<Double>的子类型。

  2.   
  3. List<Number>不是List<Dog>的子类型,因为您可以将List<Animal>添加到Cat,但您无法使用List<Animal>执行此操作}。

  4.   

那是对的。泛型不是协变的(但数组是!)。这里有一些跟进阅读:Why are arrays covariant but generics are invariant?

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

这是事实,但DoubleList<Number>之间存在重要差异。您可以将List<? extends Number>视为特定List<? extends Number> - 子类型的列表(NumberList<Double>List<Integer>,...之一,并且List<Long>作为列表,可能包含List<Number>Double,...

的混合

至于你的最后一个问题:

  

Integer的子类型是List<Double> ...

是的,你可以拥有

List<? extends Number>
  

......为什么?

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

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

答案 1 :(得分:6)

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

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

(1) - TU类型在之前的陈述中有所不同

答案 2 :(得分:6)

还应该添加几点

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

  2. 尽量不要将泛型参数视为“子类型”,泛型参数实际上意味着“使用泛型参数”所以在此case“ list 使用 Number ”。一个很好的例子就是HashMap source,如果你仔细研究它的内部工作原理,它实际上存储了一个Entry数组,并且所有条目都存储了密钥和值。当你看到泛型的更复杂的用法时,你偶尔会看到这种用法。

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

    public class DummyIterator<O> implements Iterator<O>{
        public boolean hasNext() {
            return false;
        }
    
        public O next() {
            return null;
        }
    }
    
  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);
    

    因此我们无法向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);
    

    您无法将List<Double>分配给有意义的List<Number>,因为您明确告诉它,该列表仅使用数字类型

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

    boolean equalListSizes(List<?> list1, List<?> list2) {
      return list1.size() == list2.size();
    }
    

    您只能在使用generic参数不修改对象的情况下使用? extends Number类型的格式。例如:

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

    <T extends Number> T firstItem(List<T> list1) {
      return list1.get(0);
    }
    

    班级:

    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();
    
  6. 作为围绕泛型的奖励思想,您还可以参数化方法以返回特定类。一个很好的例子是junit中的any()方法和空列表集合:

    Dog rufus = Matchers.<Dog>any();
    List<Dog> dogs = Collections.<Dog>emptyList();
    

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

答案 3 :(得分:3)

它帮助我将泛型视为约束或契约,而不是具有子类型的类型。 因此变量List<? extends Number> var表示:var是一个未知类型?的列表,它被约束为Number的子类型。

List<Number> listN;
List<Double> listD;
List<? extends Number> listX;
...
Number n1 = ...;
Double d1 = ...;
...
listN.add(n1); // OK n1 is a Number
listN.add(d1); // OK d1 is a Double, which is a Number
listD.add(n1); // compile error, n1 is not a Double
listD.add(d1); // OK
listX.add(n1); // compile error, because the exact type of list is not known! (prevents putting a Dog in a Cat list)
listX.add(d1); // compile error, same cause

所以当你甚至无法将一个号码放入List<? extends Number>时,这个列表的目的是什么?它允许您使用精确类型与手头任务无关的列表:

// instead of several exactly typed methods...
int count(List<Number> numberList) {...}
int count(List<Object> objectList) {...}
// ...etc. you can have one with a degree of freedom:
int count(List<?> anyList) {...} // don't need to know the exact type of list

// instead of this...
Number sum(List<Number> numberList) {...}
Number sum(List<Double> doubleList) {...}
Number sum(List<Integer> integerList){...}
// you can do this, with a little less freedom in the ?
Number sum(List<? extends Number> list) {
  // the only thing we need to know about the list's type is that it is some number type
  ...
  Number ni = list.get(i);
  ...
}

使用通配符? extends X可以放松刚性合约到较弱的条件。

使用命名类型参数,您可以在多个变量之间建立对允许类型的约束:

// works for any type T of list, whatever T is
// T is the same in the argument and in the return
<T> T pickObject(List<T> list, int index) {
  return list.get(index);
}

// works for any type T of list, if T is a Number type
// T is the same in the argument and in the return
<T extends Number> T pickNumber(List<T> list, int index) {
  return list.get(index);
}
...
List<Number> list;
Number n = pickNumber(list);