I'm trying to use java.util.Function
object as an input to a library class.
Function<? extends MyEntity, List> mapper;
public MyLibraryClass(Function<? extends MyEntity,List> mapper) {
...
}
public void aMethod(ClassExtendsMyEntity e) {
mapper.apply(e);
}
The line mapper.apply(e)
doesn't compile.
If I change to use ? super
instead of ? extends
, then the fail will be in the client using this library in the following code:
Function<ClassExtendsMyEntity, List> mapper = e -> e.getListObjects();
new MyLibraryClass(mapper);
Can anyone help explain why getting this issue and if there is a way to get this working?
CodePudding user response:
The declaration Function<? extends MyEntity,List> mapper
means:
mapper
expects an argument of some type X
which is a subtype of MyEntity
. The problem is, it doesn't tell what type it is.
And therefore the compiler can't verify if your call passing an instance of type ClassExtendsMyEntity
is valid. After all X
might be SomeOtherClassExtendingMyEntity
.
In order to fix this just make the function type Function<MyEntity,List>
CodePudding user response:
Function<? extends MyEntity,List> mapper;
This does not say "any subclass of MyEntity
can be passed to mapper
". It says "there exists some subclass of MyEntity
that can be passed to mapper
". It might be ClassExtendsMyEntity
, it might be MyEntity
itself, or it might be some crazy class that you've never even heard of.
Function<T, S>
provides one function: S apply(T arg)
. Since your argument type is ? extends MyEntity
, the only valid argument you can pass to mapper.apply
is a value which is of every subtype of MyEntity
at once, and there is no value that satisfies that condition.
Put more technically, the first argument of Function<T, S>
is, in principle, contravariant, so using the covariant ? extends T
annotation makes little sense on it.
Depending on what you want, there are two solutions. If your function works for any MyEntity
, then you should simply write Function<MyEntity, List>
and forgo the generics/wildcards altogether. On the other hand, if you want MyLibraryClass
to support only one subtype, but a subtype that you know about, then you need to make that a generic argument to MyLibraryClass
.
public class MyLibraryClass<T extends MyEntity> {
Function<? super T, List> mapper;
public MyLibraryClass(Function<? super T, List> mapper) {
...
}
public void aMethod(T e) {
mapper.apply(e);
}
}
Again, the first type argument of Function
is, in principle, contravariant, so we should use ? super T
to bound it by our type argument T
. That means that if I have a MyLibraryClass<ClassExtendsMyEntity>
, the function inside that might be a Function<ClassExtendsMyEntity, List>
, or a Function<MyEntity, List>
, or a Function<Object, List>
, since all of those support ClassExtendsMyEntity
arguments. For more on the reasoning behind that, see What is PECS.