通用参数返回流中的List <ChildInterface>,但是直接方法调用返回List <Parent>

Nat*_*H06 1 generics lambda list generic-list java-8

对于设置,我有:

interface Parent

interface Child1 extends Parent

interface Child2 extends Parent
Run Code Online (Sandbox Code Playgroud)

我在其他地方:

public class MyClass {
    private List<Child1> child1List = new ArrayList<>();

    public List<Parent> getChild1List(Contact contact) {
        return child1List.parallelStream()
                         .filter(m -> m.getContacts().contains(contact))
                         .sorted(Comparator.comparing(Parent::getParentField))
                         .collect(Collectors.toList());
    }
}
Run Code Online (Sandbox Code Playgroud)

当我这样做时,getChild1List返回一个List<Parent>(不应该返回List<Child1>吗?)

后来,我发现该流对其他方法很有用,所以我提取它并用它构建了一个泛型方法.我有多个扩展Parent的接口,所以我做了如下:

    private <T extends Parent> List<T> returnsListByContact(List<T> childList, Contact contact) {
        return childList.parallelStream()
                        .filter(m -> m.getContacts().contains(contact))
                        .sorted(Comparator.comparing(Parent::getParentField))
                        .collect(Collectors.toList());
    }
Run Code Online (Sandbox Code Playgroud)

getChild1List(Contact contact)成为:

    public List<Parent> getChild1List(Contact contact) {
        return returnsListByContact(child1List, contact);
    }
Run Code Online (Sandbox Code Playgroud)

但是现在它不喜欢这个,引用getChild1List返回一个List<Child1>.我不明白为什么,因为流的实现根本没有改变 - 除了启动它的childList来自通用参数,而不是直接调用MyClass的私有成员字段.

那他们为什么要回归两件不同的东西呢?

Stu*_*rks 5

(这个例子令人困惑.Meeting真的Parent吗?)

在的第一个版本getChild1List,所述collect(toList())方法被称为上Stream<Child1>和它的目标类型-通过的返回类型确定getChild1List-是List<Parent>.这是有效的,因为它允许T超类型的收集器收集类型为T的流.更具体地说,您将类型的实例添加Child1List<Parent>类型安全且允许的类型中.您也可以改变getChild1List()返回的声明List<Child1>而不是List<Parent>.

您可以通过查看collect()in 的声明来查看允许差异的位置Stream<T>:

<R,A> R collect(Collector<? super T,A,R> collector)
Run Code Online (Sandbox Code Playgroud)

? super T是允许方差的原因.

你的声明returnsListByContact,

<T extends Parent> List<T> returnsListByContact(List<T> childList, ...)
Run Code Online (Sandbox Code Playgroud)

没有允许偏差.它需要一个类型的参数List<T>并返回一个List<T>.参数和返回类型必须相同.这就是为什么当你传入一个List<Child1>并且尝试从返回类型为的方法返回它时不匹配的原因List<Parent>- 那些类型是不兼容的.

要解决此问题,您需要在声明中添加一些差异returnsListByContact.这是我如何做到的:

<T extends Parent> List<T> returnsListByContact(List<? extends T> childList, ...)
Run Code Online (Sandbox Code Playgroud)

这允许您在传递某个子类型的列表时返回某种类型的列表,在这种情况下,在List<Parent>传入时返回List<Child1>,这是我认为您想要的.

  • 再加一句第一句,我也无法理解. (2认同)