Disambiguate overloaded methods that accept different functional interfaces

matthiasbe :

I have the following available methods in a Utils class:

protected <U> U withTx(Function<OrientGraph, U> fc) {
    // do something with the function
}

protected void withTx(Consumer<OrientGraph> consumer) {
    withTx(g -> {
        consumer.accept(g);
        return null;
    });
}

And in a method of myClass I have:

withTx(g -> anotherMethod(g));

The second piece of code has a compilation error:

The method withTx(Function<OrientGraph, Object>) is ambiguous for the type myClass

I guess this comes from the compiler, which is not able to determine if the lambda is a Consumer or a Function. Is there a noble way to disambiguate this situation?

Whatever the method anotherMethod returns (void, Object, anything), I don't want to use this return value.

One solution is to do:

withTx(g -> { anotherMethod(g); });

But I wanted to know if there was something better, because this triggers SonarLint.

Oleksandr Pyrohov :

From the "Effective Java" by Joshua Bloch:

Do not provide a method with multiple overloadings that take different functional interfaces in the same argument position if it could create a possible ambiguity in the client.

The easiest way to avoid this problem is not to write overloadings that take different functional interfaces in the same argument position.

One more possible solution could be to use a different names for these two methods:

<U> U withTxFunction(Function<OrientGraph, U> fc);

void withTxConsumer(Consumer<OrientGraph> consumer);

A good example of this approach one can find in the Java API itself, for example in the  IntStream interface:

mapToDouble(IntToDoubleFunction mapper);

mapToLong(IntToLongFunction mapper);

Update:

I'd like to add a clarification of why does the compiler complain? That is because...

The lambda g -> anotherMethod(g) can be assigned to both Function<T, R> and Consumer<T>:

Function<T, R> func  = g -> anotherMethod(g);

Consumer<T> consumer = g -> anotherMethod(g); // here you just ignore the return value

<T> T anotherMethod(T t) { ... }

So, when you write withTx(g -> anotherMethod(g)), you get the "Ambiguous method call" error, the compiler fails to find out which of the overloaded method should be used:

withTx(function) OR withTx(consumer) ... ?

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=439836&siteId=1