Home > Back-end >  Java Function interface with bounded wildcard
Java Function interface with bounded wildcard

Time:08-19

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.

  • Related