zoukankan      html  css  js  c++  java
  • 从数学的角度理解函数式接口

    函数式接口

    函数接口是只有一个抽象方法的接口,此方法是行为的抽象,将行为作为入参,进而在面对对象编程的基础上添加面向函数编程的方式。

    接下来,我们以数学的角度来研究一下常见的5个函数式接口,理解了这几个,剩下的也就理解了。
    复制代码

    1. Function

    • Function接口 -> 接收一个参数并返回一个结果, 类似于一元函数 -> y=f(x)
    • 面向函数编程时要理解"传递行为", 进一步抽象因而变得更灵活
    • 理解: "f.compose(g).apply(x)" -> "y=f(g(x))" 和"f.andThen(g).apply(x)" -> "y=g(f(x))"
     1 public class FunctionTest {
     2 
     3     /**
     4      * Test 01.
     5      */
     6     @Test
     7     public void test01() {
     8 
     9         FunctionTest test = new FunctionTest();
    10 
    11         System.out.println(test.compute(1, value -> 5 + value));
    12         System.out.println(test.compute(3, value -> value * value));
    13 
    14         System.out.println(test.method1(2));
    15         System.out.println(test.method2(2));
    16 
    17         Function<Integer, Integer> function = value -> value * 2;
    18         System.out.println(test.compute(4, function));
    19     }
    20 
    21     /**
    22      * Compute int.
    23      * 函数式编程写法: 将行为作为入参, 多种行为抽象为一个方法
    24      *
    25      * @param x        自变量x
    26      * @param function 函数执行体
    27      * @return 因变量f(x) int
    28      */
    29     public int compute(int x, Function<Integer, Integer> function) {
    30         return function.apply(x);
    31     }
    32 
    33     /**
    34      * 传统用法: 将行为写在方法体, 多种行为就得写多个方法
    35      *
    36      * @param a the a
    37      * @return the int
    38      */
    39     public int method1(int a) {
    40         return 5 + a;
    41     }
    42 
    43     /**
    44      * Method 2 int.
    45      *
    46      * @param a the a
    47      * @return the int
    48      */
    49     public int method2(int a) {
    50         return a * a;
    51     }
    52 
    53     /* -------------------------------------------------------------- */
    54 
    55     /**
    56      * Test 02.
    57      */
    58     @Test
    59     public void test02() {
    60 
    61         FunctionTest test = new FunctionTest();
    62 
    63         /* y=f(g(2))=f(2*2)=f(4)=12 */
    64         System.out.println(test.compute(2, value -> value * 3, value -> value * value));
    65         /* y=g(f(2))=g(2*3)=g(6)=36 */
    66         System.out.println(test.compute2(2, value -> value * 3, value -> value * value));
    67     }
    68 
    69     /**
    70      * compose操作
    71      *
    72      * @param x         x
    73      * @param function1 f(x)
    74      * @param function2 g(x)
    75      * @return the int  y=f(g(x))
    76      */
    77     public int compute(int x, Function<Integer, Integer> function1, Function<Integer, Integer> function2) {
    78         return function1.compose(function2).apply(x);
    79     }
    80 
    81     /**
    82      * andThen操作
    83      *
    84      * @param x         x
    85      * @param function1 f(x)
    86      * @param function2 g(x)
    87      * @return the int  y=g(f(x))
    88      */
    89     public int compute2(int x, Function<Integer, Integer> function1, Function<Integer, Integer> function2) {
    90         return function1.andThen(function2).apply(x);
    91     }
    92 }

    2. BiFunction

    • 函数式接口BiFunction: 接收两个参数, 返回一个值. 类似于二元函数: y=f(x1,x2)
    • 定义BiFunction的行为, 即apply()的实现:(param1,param2) -> { ... }
    • 理解andThen()方法:"f.andThen(g).apply(x1,x2)" -> "y=g(f(x1,x2))". 其中 f为BiFunction, g为Function. 由 x1, x2根据函数 f计算得到中间结果, 再根据函数 g计算得到最终结果
    • 理解为啥 BiFunction没有compose()方法: "f.compose(g).apply(x1,x2)" -> 先执行函数 g得到一个值, 再作为函数 f的入参, 得到最终结果, 但是 f是BiFunction, 需要两个自变量, 产生矛盾, 因此 compose()是不合理的.
    • 在面向对象编程的基础上(对一段执行的逻辑进行抽象, 即方法), 把对象的方法进一步抽象, 即尽量使用函数式接口作为入参, 在使用方法时再定义行为, 优雅, 建议使用.
    public class BiFunctionTest {
    
        /**
         * 测试函数式接口BiFunction的实现方法:
         * R apply(T t, U u);
         */
        @Test
        public void test01() {
    
            BiFunctionTest test = new BiFunctionTest();
    
            int add = test.compute(1, 2, Integer::sum);
            System.out.println("1+2 = " + add);
            int sub = test.compute(7, 4, (value1, value2) -> value1 - value2);
            System.out.println("7-4 = " + sub);
            int mul = test.compute(3, 5, (value1, value2) -> value1 * value2);
            System.out.println("3*5 = " + mul);
            int div = test.compute(8, 4, (value1, value2) -> value1 / value2);
            System.out.println("8/4 = " + div);
    
        }
    
        /**
         * @param x1         自变量 x1
         * @param x2         自变量 x2
         * @param biFunction 函数体f(x1,x2)
         * @return int 因变量 y=f(x1,x2)
         */
        private int compute(int x1, int x2, BiFunction<Integer, Integer, Integer> biFunction) {
            return biFunction.apply(x1, x2);
        }
    
        /* -------------------------------------------------------------- */
    
        /**
         * Test 03.
         */
        @Test
        public void test03() {
    
            Person person1 = new Person("zhangsan", 20);
            Person person2 = new Person("lisi", 30);
            Person person3 = new Person("wangwu", 40);
    
            List<Person> persons = Arrays.asList(person1, person2, person3);
    
            BiFunctionTest test = new BiFunctionTest();
    
            /* 根据username筛选 <- 将行为写死在方法里(不建议使用) */
            List<Person> personResult1 = test.getPersonsByUsername("zhangsan", persons);
            personResult1.forEach(person -> System.out.println(person.getUsername()));
            System.out.println("-------------------------------------");
    
            /* 根据年龄筛选 <- 将行为写死在方法里(不建议使用) */
            List<Person> personResult2 = test.getPersonsByAge(20, persons);
            personResult2.forEach(person -> System.out.println(person.getAge()));
            System.out.println("-------------------------------------");
    
            /* 根据年龄筛选 <- 将行为抽象出来, 因此更加灵活(建议使用) */
            List<Person> personResult3 = test.getPersonsByAge2(20, persons,
                    (age, personList) -> personList.stream()
                            .filter(person -> person.getAge() > age)
                            .collect(Collectors.toList()));
            personResult3.forEach(person -> System.out.println(person.getAge()));
    
            /* 使用方法时再定义行为 */
            List<Person> personResult4 = test.getPersonsByAge2(20, persons,
                    (age, personList) -> personList.stream()
                            .filter(person -> person.getAge() <= age)
                            .collect(Collectors.toList()));
            personResult4.forEach(person -> System.out.println(person.getAge()));
        }
    
    
        /* -------------------------------------------------------------- */
    
        /**
         * 测试函数式接口BiFunction的默认方法:
         * default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after)
         */
        @Test
        public void test02() {
            BiFunctionTest test = new BiFunctionTest();
    
            int result = test.compute2(6, 5, (value1, value2) -> value1 - value2, value -> value * 10);
            System.out.println("(6-5)*10 = " + result);
    
        }
    
        /**
         * @param x1         自变量 x1
         * @param x2         自变量 x2
         * @param biFunction 函数体y=f(x1,x2)
         * @param function   函数体y=g(x)
         * @return int 因变量 y=g(f(x1,x2))
         */
        private int compute2(int x1, int x2, BiFunction<Integer, Integer, Integer> biFunction, Function<Integer, Integer> function) {
            return biFunction.andThen(function).apply(x1, x2);
        }
    
        /**
         * @param username 参数x1
         * @param persons  参数x2
         * @return y
         */
        private List<Person> getPersonsByUsername(String username, List<Person> persons) {
            /* 流式处理 */
            return persons.stream().filter(person -> person.getUsername().equals(username)).
                    collect(Collectors.toList());
        }
    
        /**
         * @param age     参数x1
         * @param persons 参数x2
         * @return y
         */
        private List<Person> getPersonsByAge(int age, List<Person> persons) {
            /* 将函数式接口定义在方法里, 行为里用流式处理 */
            BiFunction<Integer, List<Person>, List<Person>> biFunction =
                    (ageOfPerson, personList) -> personList.stream()
                            .filter(person -> person.getAge() > ageOfPerson)
                            .collect(Collectors.toList());
            return biFunction.apply(age, persons);
        }
    
        /**
         * 将具体行为作为入参, 即对此方法进行进一步抽象
         *
         * @param age        参数x1
         * @param persons    参数x2
         * @param biFunction f(x1,x2)
         * @return y=f(x1,x2)
         */
        private List<Person> getPersonsByAge2(int age, List<Person> persons, BiFunction<Integer, List<Person>, List<Person>> biFunction) {
            return biFunction.apply(age, persons);
        }
    
    }
    
    @Data
    @AllArgsConstructor
    class Person {
    
        private String username;
    
        private int age;
    
    }

    3. Supplier

    • 函数式接口 Supplier不接受参数, 返回一个值 类似于常数函数: y = a
    public class SupplierTest {
    
        /**
         * 用了函数式接口Supplier之前, 处理 n个行为就得有 n个方法
         */
        @Test
        public void test01() {
    
            SupplierTest test = new SupplierTest();
    
            System.out.println(test.getValue1());
            System.out.println(test.getValue2());
            System.out.println(test.getValue3());
    
        }
    
        private String getValue1() {
            return "hello world!";
        }
    
        private int getValue2() {
            int a = 665;
            return a + 1;
        }
    
        private Object getValue3() {
            return new Object();
        }
    
        /* -------------------------------------------------------------- */
    
        /**
         * 用了函数式接口Supplier之后, n个行为, 但是仅定义一个方法
         */
        @Test
        public void test02() {
    
            SupplierTest test = new SupplierTest();
    
            System.out.println(test.getValue(() -> "hello world!"));
            System.out.println(test.getValue(() -> {
                int a = 665;
                return a + 1;
            }));
            System.out.println(test.getValue(() -> new Object()));
    
        }
    
        /**
         * 用Supplier作为入参, 进一步抽象
         *
         * @param supplier 定义行为
         * @return the value
         */
        private <T> T getValue(Supplier<T> supplier) {
            return supplier.get();
        }
    }

    4. Consumer

    • 函数式接口 Consumer: 接收一个参数, 但是不返回任何结果 理解为 void = f(x)
    • andThen() : void = f(x) & g(x)
    public class ConsumerTest {
    
       /**
        * 传统方式
        */
       @Test
       public void test01() {
    
           ConsumerTest test = new ConsumerTest();
    
           test.handle1("qwe");
           test.handle2(5);
           List<Integer> list = Arrays.asList(1, 3, 5);
           test.handle3(list);
    
       }
    
       private void handle1(String str) {
           int length;
           length = str.length();
           System.out.println(length);
       }
    
       private void handle2(int a) {
           System.out.println(a + a);
       }
    
       private void handle3(List<Integer> lists) {
           System.out.println(lists);
       }
    
       /* -------------------------------------------------------------- */
    
       /**
        * 测试函数式接口Consumer
        */
       @Test
       public void test02() {
    
           ConsumerTest test = new ConsumerTest();
    
           test.handle("qwe", x -> System.out.println(x.length()));
           test.handle(5, x -> System.out.println(x + x));
           List<Integer> list = Arrays.asList(1, 3, 5);
           test.handle(list, System.out::println);
    
           /* 测试andThen()方法 */
           test.handle2("twtwtwtwtw", x -> System.out.println(x.substring(1, 3))
                   , y -> System.out.println(y.length()));
    
       }
    
       /**
        * @param x        参数x
        * @param consumer 行为
        * @param <T>      泛型 T
        */
       private <T> void handle(T x, Consumer<T> consumer) {
           consumer.accept(x);
       }
    
       /**
        * void = f(x) & g(x)
        *
        * @param x         参数x
        * @param consumer  行为
        * @param consumer2 行为2
        * @param <T>       泛型 T
        */
       private <T> void handle2(T x, Consumer<T> consumer, Consumer<T> consumer2) {
           consumer.andThen(consumer2).accept(x);
       }
    
    }

    5. Predicate

    • 函数式接口 Predicate 接收一个参数, 返回一个布尔值 类似于 y=f(x), 其中y∈{true, false}
    • 理解 test()、and()、negate()、or()
    public class PredicateTest {
    
        /**
         * 测试函数式接口 Predicate
         */
        @Test
        public void test() {
    
            PredicateTest test = new PredicateTest();
    
            List<Integer> numList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    
            System.out.println(test.judge(1, numList::contains));
            System.out.println("--------------- 取奇数 ---------------------------");
            test.judgeList(numList, num -> num % 2 == 1);
            System.out.println("--------------- 取偶数 && 大于5的数 ---------------");
            test.judgeList2(numList, num -> num % 2 == 0, num -> num > 5);
            System.out.println("--------------- 取偶数 ---------------------------");
            test.judgeList3(numList, num -> num % 2 == 1);
            System.out.println("--------------- 取偶数 || 大于5的数 ---------------");
            test.judgeList4(numList, num -> num % 2 == 0, num -> num > 5);
    
        }
    
        private Boolean judge(int x, Predicate<Integer> predicate) {
            return predicate.test(x);
        }
    
        private void judgeList(List<Integer> list, Predicate<Integer> predicate) {
            for (Integer i : list) {
                if (predicate.test(i)) {
                    System.out.println(i);
                }
            }
        }
    
        private void judgeList2(List<Integer> list, Predicate<Integer> predicate1, Predicate<Integer> predicate2) {
            for (Integer i : list) {
                if (predicate1.and(predicate2).test(i)) {
                    System.out.println(i);
                }
            }
        }
    
        private void judgeList3(List<Integer> list, Predicate<Integer> predicate) {
            for (Integer i : list) {
                if (predicate.negate().test(i)) {
                    System.out.println(i);
                }
            }
        }
    
        private void judgeList4(List<Integer> list, Predicate<Integer> predicate1, Predicate<Integer> predicate2) {
            for (Integer i : list) {
                if (predicate1.or(predicate2).test(i)) {
                    System.out.println(i);
                }
            }
        }
    
    }

    在充分理解函数式接口之后,写lambda变得很轻松,然后结合stream流就可以写出优雅的代码了。在业务代码中用stream和lambda真的很爽...

  • 相关阅读:
    Oracle数据库导入(数据泵导)
    C# 根据WCF 的url动态调用WCF
    winform嵌套浏览器
    微信支付服务商模式 配置
    结对项目-增强型科学计算器
    vscode编辑远程linux系统下c/c++代码实现代码补全
    Linux development with C++ in Visual Studio
    用VS2015开发Linux程序详细教程-配置篇
    Go语言环境搭建详解(2020版)
    bat脚本实现后台运行cmd命令
  • 原文地址:https://www.cnblogs.com/q964024886/p/12820385.html
Copyright © 2011-2022 走看看