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
Meeting
reallyParent
?)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 typeChild1
to 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 T
is 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.