Javac Bug?
Vicente-Arturo Romero-Zaldivar
vicente.romero at oracle.com
Fri Nov 15 05:57:31 PST 2013
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