Javac Bug?

Zhong Yu zhong.j.yu at gmail.com
Wed Nov 13 11:04:46 PST 2013


This program does not compile in javac8 build 115. I think it should
compile (but who knows really:)

public class Bug<T> {
    public void forEach(Consumer<? super T> consumer) {
        // ...
    }
    public void forEach(Predicate<? super T> predicate) {
        // ...
    }
    public void forEach(BiConsumer<? super Integer, ? super T> biConsumer) {
        forEach((v) -> { biConsumer.accept(1, v); });
    }
    public void forEach(BiPredicate<? super Integer, ? super T> biPredicate) {
        forEach(v -> { return biPredicate.test(1, v); } );
    }
}



On Wed, Nov 13, 2013 at 12:59 PM, Zhong Yu <zhong.j.yu at gmail.com> wrote:
> Here's my understanding how the process works.
>
> When javac sees
>
>     forEach( v->... )
>
> two pieces of information of the implicit lambda are used to prune
> overloading methods:
> 1. the arity. therefore BiConsumer/BiPredicate are excluded
> 2. whether the lambda body returns void or a real type.
>
> If the lambda body is a block, it's quite clear whether it's void or
> not. It seems to me that this would work:
>
>     public void forEach(BiConsumer<? super Integer, ? super T> biConsumer) {
>         forEach((v) -> { biConsumer.accept(1, v); });
>     }
>     public void forEach(BiPredicate<? super Integer, ? super T> biPredicate) {
>         forEach(v -> { return biPredicate.test(1, v); } );
>     }
>
> ( ... But it fails in javac build 115. javac bug?)
>
> If the lambda body is a "statement expression", like in your original example
>
>     public void forEach(BiConsumer<? super Integer, ? super T> biConsumer) {
>         forEach((v) -> biConsumer.accept(1, v) );
>     }
>     public void forEach(BiPredicate<? super Integer, ? super T> biPredicate) {
>         forEach(v -> biPredicate.test(1, v) );
>     }
>
> It's unclear what the return type is, so javac cannot use the return
> type to prune. Note that even though `biConsumer.accept(1, v)`
> obviously returns void, javac does not know that, because the type of
> `v` is not known yet. And even though `biPredicate.test(1, v)`
> obviously returns boolean, it is compatible to a function type that
> expects void return.
>
> Now, for each invocation, we have two potentially applicable methods.
>
> The next step tries to prune methods by checking argument types
> against method parameter types. Unfortunately, implicit lambda
> expressions are too vague, so they are simply ignored in this step.
>
> Therefore both methods are considered applicable methods. The most
> specific method is sought after. However neither method is more
> specific than the other, therefore we see the error message of
> ambiguity.
>
> If you use explicit lambda expressions, javac is able to do more
> pruning, so this works:
>
>     public void forEach(BiConsumer<? super Integer, ? super T> biConsumer) {
>         forEach((T v) -> biConsumer.accept(1, v) );
>     }
>     public void forEach(BiPredicate<? super Integer, ? super T> biPredicate) {
>         forEach( (T v) -> biPredicate.test(1, v) );
>     }
>
> So if you have overloading methods like this
>
>     public void forEach(Consumer<? super T> consumer) {
>         // ...
>     }
>     public void forEach(Predicate<? super T> predicate) {
>         // ...
>     }
>
> client code often cannot use implicit lambda expressions as arguments;
> and experts have advised against this kind of overloading scheme (two
> functional interfaces with the same arity)
>
>
> Zhong Yu
>
> On Wed, Nov 13, 2013 at 8:01 AM, Dávid Karnok <akarnokd at gmail.com> wrote:
>> Hello,
>>
>> I'm rewriting a library and run into a strange ambiguity error reported by
>> javac in JDK 8 b115, Windows 7 x86:
>>
>> public class Bug<T> {
>>     public void forEach(Consumer<? super T> consumer) {
>>         // ...
>>     }
>>     public void forEach(BiConsumer<? super Integer, ? super T> consumer) {
>>         forEach((v) -> consumer.accept(1, v));
>>     }
>>     public void forEach(Predicate<? super T> stoppableConsumer) {
>>         // ...
>>     }
>>     public void forEach(BiPredicate<? super Integer, ? super T>
>> stoppableConsumer) {
>>         forEach(v -> stoppableConsumer.test(1, v));
>>     }
>> }
>>
>> error: reference to forEach is ambiguous
>>         forEach((v) -> consumer.accept(1, v));
>>   both method forEach(Consumer<? super T>) in Bug and method
>> forEach(Predicate<? super T>) in Bug match
>>   where T is a type-variable:
>>     T extends Object declared in class Bug
>>
>> error: reference to forEach is ambiguous
>>         forEach(v -> stoppableConsumer.test(1, v));
>>   both method forEach(Consumer<? super T>) in Bug and method
>> forEach(Predicate<? super T>) in Bug match
>>   where T is a type-variable:
>>     T extends Object declared in class Bug
>>
>> I'm using NetBeans 7.4 (Build 201310111528) which doesn't report this
>> ambiguity in the editor, and the "highlight occurrences" correctly selects
>> the 3rd method when I select the forEach in the 4th method. However, the
>> clean and build fails with the errors above. I have to add explicit cast to
>> the problematic places.
>>
>> So I'm a bit confused right now. Is it a bug with NetBeans' editor, with
>> javac or simply the artifact of how overload resolution works?
>> --
>> Best regards,
>> David Karnok
>>


More information about the lambda-dev mailing list