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