从数学的角度理解函数式接口
函数式接口
函数接口是只有一个抽象方法的接口,此方法是行为的抽象,将行为作为入参,进而在面对对象编程的基础上添加面向函数编程的方式。
接下来,我们以数学的角度来研究一下常见的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真的很爽...