Ambiguous reference

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


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