1

I have a class with an overloaded Method. I want it to either take a lambda function OR a Number which is comparable (as I have to check values of the method)

public class Factory {
    public static LambdaHandler create(Runnable fn){
         return new LambdaHandler(fn);
    }

    public static <T extends Number & Comparable<T>>  NumberHandler create(T number){
        return new NumberHandler<>(number);
    }
}

However when I try to call these methods in another class I am getting an ambiguous match error.

@Test
public void testSomething() throws Exception {
    create(() -> { }).handle();
}

Any ideas how to fix this?

Cheers,

Ben

Ben Flowers
  • 1,434
  • 7
  • 21
  • 49

1 Answers1

3

It is ambiguous because it is possible that there is a type T which could be matched by a lambda expression, so the compiler doesn't know which of the two create() methods to call when you pass it a lambda expression.

You can do a cast to Runnable:

create((Runnable) () -> { }).handle();

Or put it in a variable:

Runnable fn = () -> {};
create(fn).handle();

Or give the methods different names so that they aren't overloaded:

public class Factory {
    public static LambdaHandler createFn(Runnable fn){
         return new LambdaHandler(fn);
    }

    public static <T extends Number & Comparable<T>>  NumberHandler createNum(T number){
        return new NumberHandler<>(number);
    }
}
Jesper
  • 202,709
  • 46
  • 318
  • 350
  • 1
    But my second create is of type T extends Number & Comparable does this not restrict it to Numbers which are comparable – Ben Flowers Jan 30 '15 at 08:03
  • 1
    Runnable is an interface. So you could possibly create `class MyRunnableNumber extends Number implements Runnable,Comparable` – Terry Storm Jan 30 '15 at 09:57
  • 1
    @Terry Storm: that’s completely off-topic. The question is why calling the method with a *lambda expression* is ambiguous whereas a lambda expression can never extend `Number`. – Holger Jan 30 '15 at 11:07
  • @Holger But Terry is right. Suppose you have this class `MyRunnableNumber`, then it is ambiguous which of the two `create` methods should be called, because it's both a `Runnable` and a `Number` with `Comparable` - it could match both versions. It shows why the method call is ambiguous. – Jesper Jan 30 '15 at 11:58
  • @Holger maybe i should have added a little more context. As Jesper said. My example was just a clarification for Ben Flowers confusion – Terry Storm Jan 30 '15 at 12:28
  • 1
    @Jesper: I don’t see the point. If you make a method call with such a `MyRunnableNumber` instance, the call would be ambiguous. But that doesn’t explain why passing a lambda expression is ambiguous. You can create a class which implements `Runnable` and `Callable` at the same time, then `ExecutorService.submit` would be ambiguous with that class. Still, this does not imply that every call to `submit` was ambiguous and even passing a lambda expression to `submit` isn’t (using an up to date jdk). – Holger Jan 30 '15 at 12:58
  • When you directly pass a lambda to the method, the compiler is going to try to find a functional interface that matches the lambda. Apparently it doesn't even need a concrete type (such as `MyRunnableNumber`) to be in scope, it already complains if it is possible that there would be such a type that matches the type parameter `T` of the second version of `create`. Because at runtime there could be such a type in the classpath that wasn't there at compile time. The ambiguity happens because a lambda can match multiple functional interfaces. – Jesper Jan 30 '15 at 14:39
  • 2
    The decision which overloaded method to invoke is made by the compiler at *compile-time* and whatever classes might exist at runtime is completely irrelevant. If you want to understand the real reasoning instead of guessing, read [Stuart Marks’ answer](http://stackoverflow.com/a/21951311/2711488) where he explains that it was a deliberate decision of the language designers to separate method overload resolution and type inference to simplify the specification and implementation at the cost of failing at some not really ambiguous cases. To cite him: “This was quite a controversial decision” – Holger Jan 30 '15 at 16:32
  • @Holger Thanks, that answer by Stuart Marks is clear. – Jesper Jan 30 '15 at 18:02