Javac Bug? - fixed
Vicente-Arturo Romero-Zaldivar
vicente.romero at oracle.com
Tue Apr 22 17:32:08 UTC 2014
Hi Zhong Yu,
The bug you reported has been fixed [1]. Now javac uses the lambda body
structure to disambiguate overload resolution. This applies to implicit
lambdas. The patch analyzes the void or value compatibility of the
lambda body to discriminate between potentially applicable methods.
Thanks,
Vicente
[1] https://bugs.openjdk.java.net/browse/JDK-8029718
On 13/11/13 19:04, Zhong Yu wrote:
> 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