6

I have a functional interface that extends standard jdk function to simply the generic types. Now I want to combine two functions using andThen which is throwing compiler error

Error:(25, 25) java: method andThen in interface java.util.function.Function<T,R> cannot be applied to given types;
required: java.util.function.Function<? super ui.instrumentation.api.messaging.Message<R>,? extends V> found: ui.instrumentation.api.transformation.Transformer<T,R> reason: cannot infer type-variable(s) V (argument mismatch; ui.instrumentation.api.transformation.Transformer<T,R> cannot be converted to java.util.function.Function<? super ui.instrumentation.api.messaging.Message<R>,? extends V>)

Here is the sample code:

public interface Transformer<T,R> extends Function<Message<T>, Message<R>> {

    static <T, R> Transformer<T, R> combine2(Transformer<T, R> first, Transformer<T, R> second) {
        return first.andThen(second));
    } 
}

Is there a way to combine functions that extends standard Function interface or is there better way to do this?

3
  • 2
    Why exactly do you need the combine2 function, when andThen does literally the same thing, for the more general argument type Function? Commented May 3, 2016 at 23:07
  • So you want a function that takes a Message<T>, passes it to first, gets a Message<R> back, then somehow passes that Message<R> to second even though second needs a Message<T> not a Message<R>. Commented May 3, 2016 at 23:10
  • 2
    My recommendation: don’t use such extending interfaces. You save a few letters to type at the expense of not allowing to use existing implementations of the base interface (here Function). Thus, you need to re-implement everything, not only andThen, even trivial functions like Function.identity() can’t be used as Transformer. Learn the lesson from the other’s mistakes; despite having the same functional signature, you can’t use a Transformer<T,T>, where a UnaryOperator<Message<T>> is required (another example of not allowing the base type Function)… Commented May 4, 2016 at 8:42

2 Answers 2

5

You need to both fix your generics, and instead of using andThen, which would only return a Function, you'll be better off inlining the lambda yourself:

static <T1, T2, T3> Transformer<T1, T3> combine2(Transformer<T1, T2> first, Transformer<T2, T3> second) {
    return (Message<T1> input) -> second.apply(first.apply(input));
} 
Sign up to request clarification or add additional context in comments.

Comments

5

The first problem is that andThen takes the return value of one function and makes that the parameter type of the next function, so you need, as @LouisWasserman explains, to chain them end-to-end with the output type of one matching the input type of the next:

static <T1, T2, T3> Transformer<T1, T3> combine2(Transformer<T1, T2> first, Transformer<T2, T3> second) {

The second problem, as he also explains, is that Function.andThen, which you are calling, returns a Function, not a Transformer. Note, however, that Function and Transformer have the same shape -- single input, single output. Because of that, you can use one and then adapt it to the other with a method reference like this:

static <T1, T2, T3> Transformer<T1, T3> combine(Transformer<T1, T2> first, Transformer<T2, T3> second) {
    return first.andThen(second)::apply;
}

You don't need to create a function to do this. You can use the same technique calling Function.andThen() directly:

    Transformer<String,Integer> t1 = ...
    Transformer<Integer,Double> t2 = ...
    Transformer<Double,String> t3 = ...

    Transformer<String,String> t123 = t1.andThen(t2).andThen(t3)::apply;

2 Comments

Thanks @HankD and @LouisWasserman, that helped me get better understanding. My original goal is to combine list of many functions (transformers in eg case) by chaining them with andThen. Look I really need to override combine method for each additional transformer like this static <T1, T2, T3, T4> Transformer<T1, T4> combine(Transformer<T1, T2> first, Transformer<T2, T3> second, Transformer<T3, T4> third) { return first.andThen(second).andThen(third)::apply; } Is there a better way to do this more generically combine list of functions?
I've updated my answer to show how you might do that

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.