1

For the following three scenarios :

void check1(Function<Parent, String> function) {
    Parent p = new Parent(); 
    function.apply(p); // compiles fine
    Child c = new Child();
    function.apply(c); // compiles fine
}

void check2(Function<? super Parent, String> function) {
    Parent p = new Parent();
    function.apply(p); // compiles fine
    Child c = new Child();
    function.apply(c); // compiles fine
}

void check3(Function<? extends Parent, String> function) {
    Parent p = new Parent();
    function.apply(p); // compile time error
    Child c = new Child();
    function.apply(c); // compile time error
}

In the 3rd case, i receive compile time failure like this for both cases where Parent object or Child object is passed to the function :

The method apply(capture#21-of ? extends Parent) in the type Function is not applicable for the arguments (Parent)

My understanding till now was : extends does mean "is or extends" but above cases lead me to following queries:

  • Why does Function<? extends T> is not accepting child, parent objects ?
  • Secondly is Function<? super T> accepting both child, parent objects due to the fact that parent reference can hold child objects (Base obj=new Child()) ?

Edit: I understand the PECS issue in collections (e.g list) like stated here since there the List<? extends Parent> might get a reference to List<GrandChild>. Now, if we try to add Child object in this, it would have caused a runtime error if not caught by compiler earlier.

Similarly is the Functional interface also behaving same & how are reference maintained here ?

The functionality is explained by examples by rgettman but just want a clearer picture compared to collections.

Moreover this works, which is fine but then seems like PECS (Extends can't consume anything) shouldn't be taken literally :

<T extends Parent> void check4(List<T> myList, Function<T, String> func) {
func.apply(myList.get(0)); // compiles successfully 
}
pxm
  • 1,442
  • 1
  • 15
  • 31

2 Answers2

1
  • Why does Function is not accepting child, parent objects ?

Your argument function can be any method that takes either a Parent or a subtype of Parent, based on the ? extends upper bound. That means that check3 can accept a Function<Child, String>, to which you cannot pass a Parent. This is why there is a compiler error on the apply method; the compiler cannot guarantee type safety when the type of the parameter to the function is unknown.

There also could exist any unknown subclass of Parent, e.g. Sibling or Grandchild, which also extend Parent. This means that Child cannot be an argument of apply either. The function could be a Function<Grandchild, String>.

  • Secondly is Function accepting both child, parent objects due to the fact that parent reference can hold child objects (Base obj=new Child()) ?

This works because ? super Parent is a lower bound. This means that the function as an argument could be a Function<Parent, String> or a Function<Object, String>. Because the function may take a Parent or even an Object, one can always pass a Parent to that function and call apply, so the compilation succeeds here.

rgettman
  • 167,281
  • 27
  • 248
  • 326
0

The actual parameter to check3 might be e.g. Function<GrandChild, String>, as far as the compiler can tell, and neither Parent nor Child is a valid type for a method requiring a GrandChild.

Andreas
  • 138,167
  • 8
  • 112
  • 195