函数式接口
函数接口是只有一个抽象方法的接口,此方法是行为的抽象,将行为作为入参,进而在面对对象编程的基础上添加面向函数编程的方式。
接下来,我们以数学的角度来研究一下常见的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))"
public class FunctionTest {
/**
* Test 01.
*/
@Test
public void test01() {
FunctionTest test = new FunctionTest();
System.out.println(test.compute(1, value -> 5 + value));
System.out.println(test.compute(3, value -> value * value));
System.out.println(test.method1(2));
System.out.println(test.method2(2));
Function<Integer, Integer> function = value -> value * 2;
System.out.println(test.compute(4, function));
}
/**
* Compute int.
* 函数式编程写法: 将行为作为入参, 多种行为抽象为一个方法
*
* @param x 自变量x
* @param function 函数执行体
* @return 因变量f(x) int
*/
public int compute(int x, Function<Integer, Integer> function) {
return function.apply(x);
}
/**
* 传统用法: 将行为写在方法体, 多种行为就得写多个方法
*
* @param a the a
* @return the int
*/
public int method1(int a) {
return 5 + a;
}
/**
* Method 2 int.
*
* @param a the a
* @return the int
*/
public int method2(int a) {
return a * a;
}
/* -------------------------------------------------------------- */
/**
* Test 02.
*/
@Test
public void test02() {
FunctionTest test = new FunctionTest();
/* y=f(g(2))=f(2*2)=f(4)=12 */
System.out.println(test.compute(2, value -> value * 3, value -> value * value));
/* y=g(f(2))=g(2*3)=g(6)=36 */
System.out.println(test.compute2(2, value -> value * 3, value -> value * value));
}
/**
* compose操作
*
* @param x x
* @param function1 f(x)
* @param function2 g(x)
* @return the int y=f(g(x))
*/
public int compute(int x, Function<Integer, Integer> function1, Function<Integer, Integer> function2) {
return function1.compose(function2).apply(x);
}
/**
* andThen操作
*
* @param x x
* @param function1 f(x)
* @param function2 g(x)
* @return the int y=g(f(x))
*/
public int compute2(int x, Function<Integer, Integer> function1, Function<Integer, Integer> function2) {
return function1.andThen(function2).apply(x);
}
}
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真的很爽...