我对Java泛型如何处理继承/多态性有点困惑。

假设以下层次结构-

动物(父母)

狗-猫(儿童)

所以假设我有一个doSomething方法(列出<Animal>动物)。根据继承和多态性的所有规则,我会假设List<Dog>是List<Animal>,List<Cat>是List<Animal>-因此任何一个都可以传递给这个方法。不是这样。如果我想实现这种行为,我必须通过说doSomething(list<?extendsAnimal>动物)来明确告诉方法接受Animal的任何子类的列表。

我知道这是Java的行为。我的问题是为什么?为什么多态性通常是隐式的,但当涉及泛型时,必须指定它?


当前回答

我认为,其他答案中提到的一点是

List<Dog>不是Java中的List<Animal>

这也是事实

狗的列表是英文的动物列表(在合理的解释下)

OP的直觉运作方式——当然这是完全有效的——是后一句话。然而,如果我们运用这种直觉,我们得到的语言在其类型系统中不是Java风格的:假设我们的语言确实允许将猫添加到我们的狗列表中。这意味着什么?这意味着该列表不再是狗的列表,而仅仅是动物的列表。还有一张哺乳动物的列表和一张四足动物的列表。

换一种说法:Java中的List<Dog>在英语中并不意味着“一个狗的列表”,它的意思是“一个只有狗的列表。”。

更一般地说,OP的直觉倾向于一种语言,在这种语言中,对对象的操作可以改变其类型,或者更确切地说,对象的类型是其值的(动态)函数。

其他回答

List<Dog>不是List<Animal>的原因是,例如,您可以将猫插入List<Animate>,但不能插入List<Dog>。。。在可能的情况下,可以使用通配符使泛型更具可扩展性;例如,从List<Dog>中读取与从List<Animal>中读取类似,但不是写入。

《Java语言中的泛型》和《Java教程》中的“泛型”一节对为什么某些事物是多态的或不多态的或允许使用泛型进行了非常好、深入的解释。

您要查找的是所谓的协变类型参数。这意味着,如果在方法中可以用一种类型的对象替换另一种类型(例如,Animal可以替换为Dog),那么同样的情况也适用于使用这些对象的表达式(因此List<Animal>可以替换为List<Dog>)。问题是,一般来说,协方差对于可变列表来说是不安全的。假设您有一个List<Dog>,并且它被用作List<Animal>。当你试图将一只猫添加到这个列表<动物>中时会发生什么?自动允许类型参数为协变会破坏类型系统。

添加语法以允许将类型参数指定为协变将是有用的,这避免了?在方法声明中扩展了Foo,但这确实增加了额外的复杂性。

我们还应该考虑编译器如何威胁泛型类:每当我们填充泛型参数时,“实例化”一个不同的类型。

因此,我们有ListOfAnimal、ListOfDog、ListOfCat等,它们是不同的类,当我们指定泛型参数时,最终由编译器“创建”。这是一个扁平的层次结构(实际上关于List根本不是一个层次结构)。

在泛型类的情况下,协方差没有意义的另一个论点是,基本上所有类都是相同的-都是List实例。通过填充泛型参数来专门化List并不会扩展类,它只会使其适用于特定的泛型参数。

答案和其他答案都是正确的。我将用我认为会有帮助的解决方案来补充这些答案。我认为这在编程中经常出现。需要注意的一点是,对于集合(列表、集合等),主要问题是添加到集合。这就是问题所在。即使移除也可以。

在大多数情况下,我们可以使用集合<?扩展T>而不是集合<T>,这应该是首选。然而,我发现这样做并不容易。关于这是否始终是最好的做法,这是一个值得争论的问题。我在这里介绍一个类DownCastCollection,它可以转换集合<?将T>扩展到集合<T>(我们可以为List、Set、NavigableSet等定义类似的类),以便在使用标准方法时使用。下面是一个如何使用它的示例(在这种情况下,我们也可以使用Collection<?extendsObject>,但我保持简单,以说明使用DownCastCollection。

/**Could use Collection<? extends Object> and that is the better choice. 
* But I am doing this to illustrate how to use DownCastCollection. **/

public static void print(Collection<Object> col){  
    for(Object obj : col){
    System.out.println(obj);
    }
}
public static void main(String[] args){
  ArrayList<String> list = new ArrayList<>();
  list.addAll(Arrays.asList("a","b","c"));
  print(new DownCastCollection<Object>(list));
}

现在开始上课:

import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;

public class DownCastCollection<E> extends AbstractCollection<E> implements Collection<E> {
private Collection<? extends E> delegate;

public DownCastCollection(Collection<? extends E> delegate) {
    super();
    this.delegate = delegate;
}

@Override
public int size() {
    return delegate ==null ? 0 : delegate.size();
}

@Override
public boolean isEmpty() {
    return delegate==null || delegate.isEmpty();
}

@Override
public boolean contains(Object o) {
    if(isEmpty()) return false;
    return delegate.contains(o);
}
private class MyIterator implements Iterator<E>{
    Iterator<? extends E> delegateIterator;

    protected MyIterator() {
        super();
        this.delegateIterator = delegate == null ? null :delegate.iterator();
    }

    @Override
    public boolean hasNext() {
        return delegateIterator != null && delegateIterator.hasNext();
    }

    @Override
    public  E next() {
        if(!hasNext()) throw new NoSuchElementException("The iterator is empty");
        return delegateIterator.next();
    }

    @Override
    public void remove() {
        delegateIterator.remove();

    }

}
@Override
public Iterator<E> iterator() {
    return new MyIterator();
}



@Override
public boolean add(E e) {
    throw new UnsupportedOperationException();
}

@Override
public boolean remove(Object o) {
    if(delegate == null) return false;
    return delegate.remove(o);
}

@Override
public boolean containsAll(Collection<?> c) {
    if(delegate==null) return false;
    return delegate.containsAll(c);
}

@Override
public boolean addAll(Collection<? extends E> c) {
    throw new UnsupportedOperationException();
}

@Override
public boolean removeAll(Collection<?> c) {
    if(delegate == null) return false;
    return delegate.removeAll(c);
}

@Override
public boolean retainAll(Collection<?> c) {
    if(delegate == null) return false;
    return delegate.retainAll(c);
}

@Override
public void clear() {
    if(delegate == null) return;
        delegate.clear();

}

}

不,列表<狗>不是列表<动物>。考虑一下你可以用列表<动物>做什么——你可以在其中添加任何动物……包括一只猫。现在,你能在一窝小狗中加入一只猫吗?绝对不是。

// Illegal code - because otherwise life would be Bad
List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
List<Animal> animals = dogs; // Awooga awooga
animals.add(new Cat());
Dog dog = dogs.get(0); // This should be safe, right?

突然你有一只非常困惑的猫。

现在,您不能将猫添加到列表<?扩展Animal>,因为你不知道它是List<Cat>。您可以检索一个值并知道它将是一个Animal,但不能添加任意的Animal。列表<?super Animal>-在这种情况下,您可以安全地将Animal添加到其中,但您不知道可能从中检索到什么,因为它可能是List<Object>。