Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 extending function interface and combining them

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?

like image 500
Raja Avatar asked May 03 '16 23:05

Raja


2 Answers

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));
} 
like image 60
Louis Wasserman Avatar answered Sep 19 '22 10:09

Louis Wasserman


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;
like image 24
Hank D Avatar answered Sep 20 '22 10:09

Hank D