是列表 List的子类 ?为什么Java泛型不是隐式多态的?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了是列表 List的子类 ?为什么Java泛型不是隐式多态的?相关的知识,希望对你有一定的参考价值。
我对Java泛型如何处理继承/多态性感到困惑。
假设以下层次结构 -
动物(父母)
狗 - 猫(儿童)
所以假设我有一个方法doSomething(List<Animal> animals)
。根据所有的继承和多态的规则,我会假设List<Dog>
是List<Animal>
而List<Cat>
是List<Animal>
- 所以任何一个都可以传递给这种方法。不是这样。如果我想实现这种行为,我必须通过说doSomething(List<? extends Animal> animals)
明确告诉方法接受Animal的任何子类的列表。
我知道这是Java的行为。我的问题是为什么?为什么多态通常是隐含的,但是当涉及泛型时必须指定它?
不,List<Dog>
不是List<Animal>
。考虑一下你可以用List<Animal>
做什么 - 你可以添加任何动物......包括一只猫。现在,你可以逻辑地将一只猫添加到一窝幼犬吗?绝对不。
// 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?
突然间你有一只非常困惑的猫。
现在,你不能将Cat
添加到List<? extends Animal>
,因为你不知道它是List<Cat>
。你可以检索一个值,并知道它将是一个Animal
,但你不能添加任意动物。 List<? super Animal>
反之亦然 - 在这种情况下,你可以安全地添加一个Animal
,但你不知道从它可以检索到什么,因为它可能是一个List<Object>
。
如果您确定列表项是给定超类型的子类,则可以使用以下方法强制转换列表:
(List<Animal>) (List<?>) dogs
当您想要在构造函数中传递列表或迭代它时,这非常有用
answer以及其他答案都是正确的。我将使用我认为有用的解决方案来添加这些答案。我认为这经常出现在编程中。需要注意的一点是,对于集合(列表,集等),主要问题是添加到集合。事情就是崩溃的地方。即使删除也行。
在大多数情况下,我们可以使用Collection<? extends T>
而不是Collection<T>
,这应该是第一选择。但是,我发现这样做并不容易。关于这是否总是最好的事情,我们有争议。我在这里介绍一个类DownCastCollection,可以将Collection<? extends T>
转换为Collection<T>
(我们可以为List,Set,NavigableSet,...定义类似的类),在使用标准方法时使用非常不方便。下面是一个如何使用它的示例(在这种情况下我们也可以使用Collection<? extends Object>
,但我保持简单,使用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();
}
}
对于参数化类型,子类型是invariant。甚至强大的类Dog
是Animal
的子类型,参数化类型List<Dog>
不是List<Animal>
的子类型。相比之下,covariant子类型由数组使用,因此数组类型Dog[]
是Animal[]
的子类型。
不变子类型确保不违反Java强制执行的类型约束。考虑@Jon Skeet给出的以下代码:
List<Dog> dogs = new ArrayList<Dog>(1);
List<Animal> animals = dogs;
animals.add(new Cat()); // compile-time error
Dog dog = dogs.get(0);
正如@Jon Skeet所说,这段代码是非法的,因为否则它会在狗预期时通过返回猫来违反类型约束。
将上述内容与数组的类似代码进行比较是有益的。
Dog[] dogs = new Dog[1];
Object[] animals = dogs;
animals[0] = new Cat(); // run-time error
Dog dog = dogs[0];
代码是合法的。但是,扔了一个array store exception。数组在运行时携带其类型,这样JVM可以强制执行协变子类型的类型安全性。
为了进一步理解这一点,让我们看看下面类的javap
生成的字节码:
import java.util.ArrayList;
import java.util.List;
public class Demonstration {
public void normal() {
List normal = new ArrayList(1);
normal.add("lorem ipsum");
}
public void parameterized() {
List<String> parameterized = new ArrayList<>(1);
parameterized.add("lorem ipsum");
}
}
使用命令javap -c Demonstration
,它显示以下Java字节码:
Compiled from "Demonstration.java"
public class Demonstration {
public Demonstration();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void normal();
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: iconst_1
5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
8: astore_1
9: aload_1
10: ldc #4 // String lorem ipsum
12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
17: pop
18: return
public void parameterized();
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: iconst_1
5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
8: astore_1
9: aload_1
10: ldc #4 // String lorem ipsum
12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
17: pop
18: return
}
观察方法体的翻译代码是相同的。编译器用erasure替换了每个参数化类型。此属性至关重要,意味着它不会破坏向后兼容性。
总之,参数化类型无法实现运行时安全性,因为编译器会通过擦除替换每个参数化类型。这使得参数化类型只不过是语法糖。
让我们以Java SE qazxsw poi为例
tutorial
那么为什么不应该将狗(圆圈)列表隐含地视为动物(形状)列表,因为这种情况:
public abstract class Shape {
public abstract void draw(Canvas c);
}
public class Circle extends Shape {
以上是关于是列表 List的子类 ?为什么Java泛型不是隐式多态的?的主要内容,如果未能解决你的问题,请参考以下文章