Javac Bug?

Zhong Yu zhong.j.yu at gmail.com
Fri Nov 15 06:51:38 PST 2013


The return type seems also play a role here, see
http://cr.openjdk.java.net/~dlsmith/jsr335-0.7.0/F.html

""
A lambda expression (15.27) is potentially compatible with a
functional interface type (9.8) if all of the following are true:

The arity of the targeted type's function type is the same as the
arity of the lambda expression.
If the targeted type's function type has a void return, then the
lambda body is either a statement expression (14.8) or a
void-compatible block (15.27.2).
If the targeted type's function type has a (non-void) return type,
then the lambda body is either an expression or a value-compatible
block (15.27.2).
""

Therefore, method
    public void forEach(Consumer<? super T> consumer)
is not potentially applicable to invocation
    forEach(v -> { return biPredicate.test(1, v); } );
because the function type of Consumer has a void return, yet the
lambda body is a value-compatible block.


On Fri, Nov 15, 2013 at 7:57 AM, Vicente-Arturo Romero-Zaldivar
<vicente.romero at oracle.com> wrote:
> 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); } );
>>      }
>> }
>
> Hi Zhong Yu,
>
> No the compiler is right functional descriptors of Consumer and Predicate
> have the same arity, so the compiler determines that there is an ambiguity
> error.
>
> Thanks,
> Vicente
>
>>
>>
>>
>> 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