How to make good use of functional interfaces | Java development practice

Opening

JDK8 is known and used as Lambda. Many people will use it, such as Stream flow, but they are simple and easy to use, such as calling the Stream API of the collection, but they will not define their own function interface or API. Today, we use several cases to improve the use of function programming in Java.

Case demonstration

Function interface description

Interfaceinput parameter Return typeexplain
UnaryOperatorTTUnary function with the same input and output types
PredicateTbooleanAssert
ConsumerT/Consume a data, only input, no output
Function<T,R>TRInput T returns R, with input and output
Supplier/TProvide a data, no input, only output
BiFunction<T,U,R>(T,U)RTwo input parameters
BiPredicate<L, R>(L,R)booleanTwo input parameters
BiConsumer<T, U>(T,U)voidTwo input parameters
BinaryOperator(T,T)TBinary function with the same input and output types

Case 1

Function<Integer, Integer> times2 = e -> e * 2;
Function<Integer, Integer> squared = e -> e * e;
// Execute the parameter first and then the caller
/*
 * 1. 4 * 4 = 16 16 * 2 = 32
 */
System.out.println("result: " + times2.compose(squared).apply(4)); // 32
/*
 * Execute the caller first: 4 * 2 = 8, and then execute the function passed in by then 8 * 8 = 64
 */
System.out.println("result: " + times2.andThen(squared).apply(4)); // 64

These two methods, andThen, are called after the Function execution outside, and compose means that before the Function executes outside, it is called.

Case 2

@Test
public void test2(){
    // Add three numbers
    Function<Integer, Function<Integer, IntFunction<Integer>>> addfun3 = x -> y -> z -> x + y + z ;
    // Add 7 numbers
    Function<Integer,
            Function<Integer,
                    Function<Integer,
                            Function<Integer,
                                    Function<Integer,
                                            Function<Integer, IntFunction<Integer>>>>>>>
            addfun7 = x -> y -> z -> a -> c -> b -> d -> x + y + z + a + c + b + d;
    // 1+2+3
    Integer sum = addfun3.apply(1).apply(2).apply(3);
    System.out.println(sum);
}

In this way, the cumulative effect of chain programming can be realized

Case 3

@SafeVarargs
private static <R> Function<R, R> combineFunctions(Function<R, R>... functions) {
    return Arrays.stream(functions)
            .reduce(Function::andThen)
            .orElseThrow(() -> new IllegalArgumentException("No functions to combine"));
}

@Test
public void test3() {
    Function<Integer, Integer> addfun2 = x -> x * x;
    final Integer apply = combineFunctions(addfun2, addfun2).apply(2);
    System.out.println(apply);

    String str = "1,2,3,4,5,6";
    Function<Object, Object> splitByComma = s -> ((String) s).split(",");
    Function<Object, Object> convertToInt = tokens -> Stream.of((String[]) tokens).map(Integer::valueOf).toArray(Integer[]::new);
    Function<Object, Object> findMax = ints -> Stream.of((Integer[]) ints).max(Integer::compare).get();
    Integer max = (Integer) combineFunctions(splitByComma, convertToInt, findMax).apply(str);
    System.out.println(max);
}

If you want to have a method that accepts variable length Function parameters, you can use Descriptor, where generics < R, R > represent input and input types. Generics are used for higher compatibility

Case 4

@Test
public void test4(){
    Function<Integer, Integer> addfun2 = x -> x * x;
    final Calculator<Integer, Integer> calculator = new Calculator<>(2);
    final Integer integer = calculator.combineFunctions(addfun2, addfun2);
    System.out.println(integer);
}

public class Calculator<R,T> {
    // Properties to be manipulated
    private Object input;

    public Calculator(Object input) {
        this.input = input;
    }
    // You can define the behavior of the object itself in this way
    @SuppressWarnings("unchecked")
    @SafeVarargs
    public final R combineFunctions(Function<T, T>... functions) {
        return (R) Arrays.stream(functions)
                .reduce(Function::andThen)
                .orElseThrow(() -> new IllegalArgumentException("No functions to combine"))
                .apply((T)input);
    }
}

Domain development patterns have been heard and understood more or less. Objects can have their own behavior in addition to their own attributes. The behavior methods for objects can also be defined by functional programming paradigm.

Case 5

// Biconsumer < T, Integer > two input parameters T, Integer
 public static <T> Consumer<T> consumerWithIndex(BiConsumer<T, Integer> consumer) {
    class Obj {
        int i;
    }
    // It will only be called once. The reason depends on Java lang.Iterable#forEach
    Obj obj = new Obj();
    // Consumer function returned
    return t -> {
        int index = obj.i++;
        // Execute system out. Println ("list [" + index + "] =" + item ") consumes the data of the specified generic type.
        consumer.accept(t, index);
    };
}

@Test
public void test5(){
    val list = Arrays.asList("Hi", "I", "am", "Henry.Yao");
    // 2 elements as a group
    val partition = Lists.partition(list, 2);
    partition.forEach(LambdaUtils.consumerWithIndex((item, index) -> {
        System.out.println("list[" + index + "]=" + item);
    }));
}

When Java 8's forEach() circulates objects, there is no way to get the object index subscript. In this case, you can declare a functional method to do it. The final writing form is similar to the forEach syntax of Scala and js, which is very useful!

java.lang.Iterable#forEach

// action: is a Consumer function
default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        /* Here, the Consumer function will be called circularly, and the content of the Consumer function returned by consumerWithIndex is
        t -> {
            int index = obj.i++;
            consumer.accept(t, index);
        }
        Therefore, Obj obj = new Obj() will only be called once, so you don't have to worry that if i is not reset to 0 during new Obj, it won't happen
        */
        consumer.accept(t, index);
    }
        action.accept(t);
    }
}
 

If I think the article is helpful to you, please praise, comment and collect. Your support is my biggest motivation!!!

Finally, Xiaobian sorted out some learning materials during the learning process, which can be shared with Java engineers and friends to exchange and learn from each other, If necessary, you can join my learning exchange group 323432957 to obtain Java architecture learning materials for free (including architecture materials of multiple knowledge points such as high availability, high concurrency, high performance and distribution, Jvm performance tuning, Spring source code, MyBatis, Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx, etc.)

Author: dingyu002

Source: dinyu002

The copyright belongs to the author. For commercial reprint, please contact the author for authorization. For non-commercial reprint, please indicate the source.

Keywords: Python Java Programming Big Data AI

Added by ericbangug on Fri, 28 Jan 2022 02:25:46 +0200