4

ComputeIfAbsent method of Map has following declaration:

computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction).

Why wasn't parameter declared as Function<K,V> when K and V types are not used anywhere else?

Now I understand that K and V are very important and they are part of the Map class declaration.

Mike
  • 812
  • 9
  • 25
  • Generally to make clear what is acceptable. For Example you can pass anything into the function as long is the super class is K and for the second parameter as long as it extends V. Else it could lead to missconceptions. – Nico Nov 14 '17 at 09:53
  • 1
    `K` and `V` is parameters of `Map` why do you think they is not used? – talex Nov 14 '17 at 09:54
  • 2
    It's worth looking [here](https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super) to start with. I'd probably go even so far as to say this question is a duplicate. – biziclop Nov 14 '17 at 12:56

2 Answers2

4

K and V are part of the type of the mapping function. So they are actually used. Specifically, they determine the acceptable and most general bounds for the argument type and the return type of the mapping function.

For

Function<? super K,? extends V> mappingFunction

this means that you can pass any function that is able to at least map everything that can serve as a key in your map (thus ? super K) and maps those keys to values that can be put into the map (thus extends V). In effect this gives you more flexibility in the choice of the mapping function.

In more theoretical terms: under sub-typing functions are contravariant in its argument type and covariant in its return type. This determines the type of mappingFunction to Function<? super K,? extends V> when regarding Map<K, V> as a function from K to V.

michid
  • 10,536
  • 3
  • 32
  • 59
4

Simple example:

Map<String,Object> myMap = ...
Function<Object,String> myFunc = Object::toString;
myMap.computeIfAbsent("42", myFunc);

Is this acceptable?

Well, given the type of myFunc is Function<Object,String>, and we have a Map<String,Object>, which sounds like there is a problem. It certainly wouldn't compile if you only accepted Function<K,V>.

But when you look at it closer, there isn't anything wrong. Not only does the code demonstrably work, it's also easy to see why it works: "42" is an instance of String, which has a toString() method. That method returns a String, which is of course an Object. So of course it's fine.

And this "fine"-ness is what is expressed by Function<? super K, ? extends V>.

biziclop
  • 48,926
  • 12
  • 77
  • 104
  • 1
    Actually, `myMap.computeIfAbsent("42", Object::toString)` still compiled, if the method’s parameter was declared `Function`, because the compiler is smart enough to bind `Object::toString` as `Function`, e.g. you could also write `Function f = Object::toString;` without getting an error. That’s why lambda expressions and method references make bad examples. Try existing functions, e.g. `computeIfAbsent(myMap, "42", Function.identity())` would not compile anymore. But then, the questions is just a duplicate of “what is PECS”… – Holger Nov 14 '17 at 13:26
  • 1
    @Holger You're of course right. I'll fix the example. – biziclop Nov 14 '17 at 13:29