3

I just started looking at Java 8 Lambda feature. I wrote this code in Java 7 and tried to execute it's equivalent in lamdas. Notice that the last line produces a Compilation error because the overloaded function is ambiguous. I understand the reason. How can I resolve this ambiguity with lambdas?

package com.java8.lambdas;

interface Bounceable{
  void bounce(double howHigh);
}

interface Fly{
  void flies(double howHigh);
}

abstract class Game{
  void play(Bounceable b) {}
  void play(Fly f) {}
}

class Ball extends Game{
   void play(Bounceable b){ b.bounce(10); }
}

class Kite extends Game{
   void play(Fly f){ f.flies(1000); }
}

public class LambdaDemo {

   public static void main(String[] args) {


   System.out.println("======= Java7: ========");
    //Ball game
    Game bg = new Ball();
    bg.play(new Bounceable(){
        @Override
        public void bounce(double howHigh) {
            System.out.println("Ball: Bouncing "+howHigh);
        }
    });

    //Kite game
    Game kg = new Kite();
    kg.play(new Fly(){
        @Override
        public void flies(double howHigh) {
            System.out.println("Kite: flies "+howHigh);
        }
    });


    System.out.println("======= Java8 Lambdas: ========");

    bg.play(x ->System.out.println("lambda: Ball bouncing "+ x));  //Ambiguous of type of Game

}

}
Ravi Ranjan
  • 740
  • 2
  • 10
  • 31
user1529412
  • 3,616
  • 7
  • 26
  • 42
  • Check out this [question](http://stackoverflow.com/questions/21905169/java8-ambiguity-with-lambdas-and-overloaded-methods). That is a duplicate of your question. I didn't read it all, but it would appear that, ultimately, what you're trying to do isn't possible. Using a generic is a workaround, but may not be desireable. – Christopher Schneider Apr 01 '16 at 17:16

2 Answers2

6

You can just indicate the correct type using a cast like syntax;

// bg.Play(Bounceable)
bg.play((Bounceable) x -> System.out.println("lambda: Ball bouncing "+ x));

// kg.Play(Fly)
kg.play((Fly) x -> System.out.println("lambda: Ball bouncing "+ x));

You can find more info on how this works in this answer.

Community
  • 1
  • 1
Joachim Isaksson
  • 176,943
  • 25
  • 281
  • 294
3

Since your lambda is an implicitly typed one (x -> ...), the compiler needs a target type to determine the type of the lambda. It can get a target type from the method signature ... usually. It has to do enough of method overload selection to determine whether that will provide a target type. In this case, it finds two possibly applicable methods (play(Bouncable) and play(Fly)), and since the functional interfaces both Bouncable and Fly have the same arity, it cannot eliminate one of them based on the arity of the lambda. So it doesn't know which one you intend to call, so it cannot use the method signature to provide a target type.

You often have several choices here for providing additional type information to disambiguate; using an explicit lambda instead of an implicit one, providing explicit type witnesses for generic method calls, or adding a cast to provide an explicit target type. In this case, only the last -- the cast -- will work for you.

The real problem is that this overload is particularly lambda-unfriendly. In fact, the compiler will tell you that, if you compile with -Wall -- you'll get a warning at the declaration of the overload that there will be ambiguities at the use site.

Brian Goetz
  • 90,105
  • 23
  • 150
  • 161