Bad interaction between wildcard and functional interface conversion

Remi Forax forax at univ-mlv.fr
Sun Jul 5 13:44:42 UTC 2015


On 07/05/2015 03:13 PM, Gernot Neppert wrote:
> Hi Remi,
>
> I think it's fair to say that I know my share of Java Generics, so I 
> generally understand the motivation of introducing wildcards into 
> method signatures.
> Just in your particular case (and in your original example),  I don't 
> see what you gain by having the "super" wildcard for the outer 
> Consumer's type parameter.
> If you leave it out, the code compiles without problems:
>
> static <K, T> Function<K, T> factory(Consumer<BiConsumer<? super K, ? 
> super T>> consumer, Function<? super K, ? extends T> ifAbsent) {
> ...
> }
>
> Can you enlighten me?

yes I can.
The java.util.function.Consumer (or its method accept if you prefer) 
acts as a consumer of T ( :) ) so using the PECS rule [1], it should be 
a Consumer<? super ...>.

The thing is that if you have a functional interface which is parametrized,
if the functional interface is parametrized by a wildcard, it stops to 
be a functional interface because you can not do any lambda/method 
reference conversion (without adding an useless cast, that will be 
removed in the generated code BTW).

So it's a stupid bug and I'm ashamed to haven't thought about it before 
the release of 8.

>
> Cheers, Gernot

regards,
Rémi

[1] https://sites.google.com/site/io/effective-java-reloaded

>
> Am 29.06.2015 15:49, schrieb Remi Forax:
>> Bitten again by the very same issue :(
>>
>> The following code doesn't compile:
>> static <K, T> Function<K, T> factory(Consumer<? super BiConsumer<? 
>> super K, ? super T>> consumer, Function<? super K, ? extends T> 
>> ifAbsent) {
>>     HashMap<K, T> map = new HashMap<>();
>>     consumer.accept(map::put);
>>     return key -> map.computeIfAbsent(key, ifAbsent);
>>   }
>>
>> I really think that it's a serious bug, the only workaround is to not 
>> use wildcards correctly, i.e.
>>   <K, T> Function<K, T> factory(Consumer<BiConsumer<? super K, ? 
>> super T>> consumer, Function<? super K, ? extends T> ifAbsent)
>>
>> cheers,
>> Rémi
>>
>> On 05/27/2015 05:29 PM, Remi Forax wrote:
>>> Hi all,
>>>
>>> The way the conversion between a lambda (or a method reference) and 
>>> a functional interface is specified doesn't take wildcard (exactly ? 
>>> super) into account making the concept of contravariance of 
>>> functional interface less intuitive that it should be.
>>>
>>> The following code compiles:
>>>   private static void create(Consumer<Consumer<String>> consumer) {
>>>     consumer.accept(s -> System.out.println(s));
>>>   }
>>>
>>> This one doesn't compile because "? super Consumer<? super String>" 
>>> is not a functional interface:
>>>   private static void create2(Consumer<? super Consumer<? super 
>>> String>> consumer) {
>>>     consumer.accept(s -> System.out.println(s));
>>>   }
>>>
>>> The workaround is to introduce a cast :(
>>>   private static void create3(Consumer<? super Consumer<? super 
>>> String>> consumer) {
>>>     consumer.accept((Consumer<String>)s -> System.out.println(s));
>>>   }
>>> which is stupid in this case because there is no ambiguity.
>>> This cast is just here because the JLS doesn't consider that ? super 
>>> Consumer<...> is a valid target type
>>>
>>> IMO, this bug is very similar to JDK-6964923 and i think the spec 
>>> should be changed to allow ? super Foo to be a valid target type for 
>>> a lambda conversion (obviously if Foo is a functional interface).
>>>
>>> regards,
>>> Rémi
>>>
>>
>
>




More information about the core-libs-dev mailing list