Javac Bug?
Vicente-Arturo Romero-Zaldivar
vicente.romero at oracle.com
Fri Nov 15 07:15:44 PST 2013
On 15/11/13 14:51, Zhong Yu wrote:
> 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.
The compiler analyze lambda expressions in several phases:
first analysis is by arity, if there are two possible applicable methods
by arity the compiler issues an error. If a method pass to the next
phase, then the rest of the information is taken into account.
>
>
> 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