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