For setup, I have :
interface Parent
interface Child1 extends Parent
interface Child2 extends Parent
And elsewhere I have:
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());
}
}
When I do it this way, getChild1List returns a List<Parent> (Shouldn't it return List<Child1>?)
Later, I found that stream to be useful for other methods, so I extracted it and built a generic method with it. I have multiple interfaces that extend Parent, so I did as follows:
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());
}
and getChild1List(Contact contact) became:
public List<Parent> getChild1List(Contact contact) {
return returnsListByContact(child1List, contact);
}
but now it doesn't like this, citing that getChild1List is returning a List<Child1>. I don't understand why, as the implementation of the stream was not altered at all - except that the childList which starts it came through a generic parameter, rather than a direct call to the private member field of MyClass.
So why do they return two different things?
(The example is confusing. Is
MeetingreallyParent?)In the first version of
getChild1List, thecollect(toList())method is called on aStream<Child1>and its target type -- determined by the return type ofgetChild1List-- isList<Parent>. This works, because it's allowed for a stream of type T to be collected by a collector of a supertype of T. More specifically, you're adding instances of typeChild1to aList<Parent>which is type-safe and is permitted. You could just as well change the declaration ofgetChild1List()to returnList<Child1>instead ofList<Parent>.You can see where the variance is allowed by looking at the declaration of
collect()inStream<T>:The
? super Tis what allows the variance.Your declaration of
returnsListByContact,does not allow variance. It takes a parameter of type
List<T>and returns aList<T>. The parameter and return type must be identical. That's why there's a mismatch when you pass in aList<Child1>and try to return it from a method whose return type isList<Parent>-- those types are incompatible.To fix this, you need to add some variance to your declaration of
returnsListByContact. Here's how I'd do it:This allows you to return a list of some type while passing in a list of some subtype, in this case returning
List<Parent>while passing inList<Child1>, which is what I think you want.