stackoverflow问题:Is List<Dog> a subclass of List<Animal>? Why aren't Java's generics implicitly polymorphic?有许多正确答案,指出您可以将Cat添加到List<Animal>
但不添加到List<Dog>
。这导致使用像List<? extends Animal>
这样的结构。我发现有些情况下这不方便,因此我已经定义了“向下转换”集合的类。 (有关DownCastCollection的示例,请参见下文)。我的问题是,你是否可以提出一些最佳方法是向下倾斜的案例?如果您认为这绝不是最佳方法,那么您能解释一下原因吗?我意识到这有点开放,但我认为答案可能非常有用,因为这种情况很常见。我同意在大多数情况下我们应该使用`Collection,但我指出有时候这不是最好的方法。您是否遇到过这不是最佳方法的示例?如果可以,您可以在此处展示它们吗?
基于https://stackoverflow.com/a/27491199/4350148中的答案,我已将DownCastCollection更改为可修改,因此现在它不会返回错误。这个问题仍然有效。
这是向下收集类:
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> {
@SuppressWarnings("rawtypes")
private Collection delegate;
public DownCastCollection(Collection<? extends E> delegate) {
if(delegate == null) throw new IllegalArgumentException();
this.delegate = delegate;
}
@Override
public int size() {
return delegate.size();
}
@Override
public boolean isEmpty() {
return delegate.isEmpty();
}
@Override
public boolean contains(Object o) {
return delegate.contains(o);
}
private class MyIterator implements Iterator<E>{
@SuppressWarnings("rawtypes")
Iterator delegateIterator;
protected MyIterator() {
super();
this.delegateIterator = delegate.iterator();
}
@Override
public boolean hasNext() {
return delegateIterator.hasNext();
}
@SuppressWarnings("unchecked")
@Override
public E next() {
return (E)delegateIterator.next();
}
@Override
public void remove() {
delegateIterator.remove();
}
}
@Override
public Iterator<E> iterator() {
return new MyIterator();
}
@SuppressWarnings("unchecked")
@Override
public boolean add(E e) {
return delegate.add(e);
}
@Override
public boolean remove(Object o) {
return delegate.remove(o);
}
@SuppressWarnings("unchecked")
@Override
public boolean containsAll(Collection<?> c) {
return delegate.containsAll(c);
}
@SuppressWarnings("unchecked")
@Override
public boolean addAll(Collection<? extends E> c) {
return delegate.addAll(c);
}
@SuppressWarnings("unchecked")
@Override
public boolean removeAll(Collection<?> c) {
return delegate.removeAll(c);
}
@SuppressWarnings("unchecked")
@Override
public boolean retainAll(Collection<?> c) {
return delegate.retainAll(c);
}
@Override
public void clear() {
if(delegate == null) return;
delegate.clear();
}
答案 0 :(得分:1)
Collections.unmodifiableCollection
及其朋友以内置方式查看Collection<? extends T>
Collection<T>
。他们确切地解决了您已正确识别的问题,可以通过禁止添加或任何类型的修改将元素添加到无法添加到Collection<T>
的{{1}}。
此问题的其他解决方案包括不依赖于基础对象的实现细节,禁止添加但不删除或复制。
最后,对于它的价值,你的解决方案与类型安全,但效率较低,而不仅仅是简单地转换集合Collection<? extends T>
,它利用擦除来简单(和不安全地)转换后备集合。
答案 1 :(得分:0)
使用泛型类型? extends E
的集合,添加新元素是不合法的,因为编译器无法验证变量指向的集合的确切类型。相反,编译器只能告诉该集合代表E
的任何子类型集合。因此:
Collection<? extends Something> collection = ...
collection.add(new Something());
无法编译。使用您的DownCastCollection
版本,该示例将编译,但会抛出运行时异常。您将编译时错误迁移为运行时错误。这不是一个好主意,因为它会为您的应用程序引入潜在的错误,否则编译器可能会遇到这些错误。
投射通用集合时的唯一原因是有道理的,当您出于某种原因知道集合的实际类型但您无法通过程序的类型表达这一点。更好的是,演员通常会创建一个新的集合:
Collection<? extends Something> collection = ...
Collection<Something> other = new HashSet<>(collection);
答案 2 :(得分:0)
如果我有一个超类或接口应该允许其子类或实现更精确地指定方法的返回值,我通常会使用List<? extends T>
。
鉴于您有以下界面。
public interface Foo {
public List<CharSequence> doSomething();
}
如果您现在想要创建一个类Bar implements Foo
,doSomething()
的方法签名必须是List<CharSequence> doSomething()
,而不是其他任何内容。
但是,如果你这样定义doSomething()
......
public interface Foo {
public List<? extends CharSequence> doSomething();
}
Bar
&#39; doSomething()
可能有一个类似于此的方法签名。
public class Bar implements Foo {
public List<String> doSomething() {
// ...
}
}
除此之外,你还应该注意@raphw所说的内容,你不能将元素添加到List<?>
或List<? extends T>
。
答案 3 :(得分:0)
这是DownCastCollection类很有用的场景。假设我有一个接口(让我们称之为IFace),它有一个返回Collection<Result>
的方法。假设Impl是IFace的一个实现。为了计算方法的结果集合,Impl使用Result接口的特定实现,称为ResultImpl并维护Collection<ResultImpl>
的列表。 ResultImpl具有不属于接口的方法,因此在没有强制转换的情况下,该方法中的Collection<Result>
维护不起作用。我的方法实现需要从Collection<Result>
返回Collection<ResultImpl>
,这是DownCastCollection有用的地方。您可能会争辩说该方法可以返回Collection<? extends Result>
,但这会改变接口IFace。换句话说,该方法需要保证返回Collection<Result>
。我们可能还希望允许方法的调用者通过添加或从中删除来修改最终结果。在这种情况下,我们仍然需要返回的结果可以修改。