关于函数式编程的一些总结

35 阅读5分钟

函数式接口

有且只有一个抽象方法的接口被称为函数式接口,函数式接口适用于函数式编程的场景,Lambda就是Java中函数式编程的体现,可以使用Lambda表达式创建一个函数式接口的对象,一定要确保接口中有且只有一个抽象方法,这样Lambda才能顺利的进行推导。

@FunctionalInterface注解

该注解可用于一个接口的定义上,一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法(equal和hashcode方法不算),否则将会报错。但是这个注解不是必须的,只要符合函数式接口的定义,那么这个接口就是函数式接口。

  1. Function(函数型接口):主要针对的则是转换**(有入参,有返回,其中T是入参,R是返回)**这个场景。
  2. Consumer(消费性接口):主要针对的是消费(1..n 入参, 无返回)。
  3. Supplier(供给型接口):主要针对的是说**获取(无入参,有返回)**这个场景。
  4. Predicate(断言型接口):主要针对的是判断(有入参,有返回,凡是返回的类型固定为Boolean。可以说Function 是包含Predicate的 )。

Function

Function<Integer, String> function =  r -> {
	return String.valueOf(r);;
}

String fun(Integer r) {
	return String.valueOf(r);
}

Java SE 8 引入了一个名为 java.util.function.Function 的函数式接口,它代表一个函数,接受一个参数并产生一个结果。包括以下4个主要的抽象方法:

  1. apply(T t):接收一个参数并产生一个结果。
  2. compose(before.apply(T t)):在apply 前执行其它操作
  3. andThen(after.apply(R r)):在apply 后执行其它操作
  4. identity():返回一个输入参数的identity 函数

您可以使用该接口定义一个函数,例如将一个字符串转换为大写:

Function<String, String> toUpperCase = string -> string.toUpperCase();

Consumer

Consumer<String> consumer = r -> {
	System.out.println(r);
}

void fun(String r){
	System.out.println(r)
}

Java SE 8 中引入了一个叫做java.util.function.Consumer 的函数式接口,它代表了一个接受一个输入参数而无返回结果的操作。它包括以下2个重要的抽象方法:

  1. accept(T t):接收一个参数,无返回值
  2. andThen(after.accept(T t)):如果这两个Consumer 实例都不是空的,则此方法会返回一个合成的Consumer ,它串行调用此Consumer实例的accept 方法,然后继续调用给定的Consumer实例的accept方法

您可以使用该接口定义接受一个字符串并将其打印到控制台的操作:

Consumer<String> consumer = r -> System.out.println(r);
consumer.accept("hello world");

Supplier

Supplier<Integer> supplier = () -> {
	return 1;
}

Integer fun() {
	return 1;
}

Java SE 8 中引入了一个名为 java.util.function.Supplier 的函数式接口,它表示结果的提供者。它会提供一个get() 方法,以便提供一个结果。

您可以使用此接口定义一个返回当前系统时间的方法:

Supplier<Long> supplier = () -> System.currentTimeMillis();
System.out.println(supplier.get());

Supplier是惰性的,只有执行了get()方法,才会执行代码块的内容。

Predicate

Predicate<String> predicate = s -> {
	return s.equals("a");
}

boolean fun(String s) {
    return s.equlas("a");
}

Java SE 8 中引入了一个叫做 java.util.function.Predicate 的函数式接口,它代表一个谓词(传入一个参数,返回一个布尔值)。它包括以下2个重要的抽象方法:

  1. test:传入一个参数,返回一个布尔值
  2. and/or/negate:将两个Predicate 连接起来

您可以使用它定义一个确定字符串长度是否大于0:

Predicate<String> predicate = (s) -> s.length() > 0;
System.out.println(predicate.test("foo"));

Lambda表达式

何为Lambda

Lambda 并不是一个什么的缩写,它是希腊第十一个字母 λ 的读音,同时它也是微积分函数中的一个概念,所表达的意思是一个函数入参和出参定义,在编程语言中其实是借用了数学中的 λ,并且多了一点含义,在编程语言中功能代表它具体功能的叫法是匿名函数(Anonymous Function)

匿名函数(英语:Anonymous Function)在计算机编程中是指一类无需定义标识符(函数名)的函数或子程序。

Lambda 在编程语言中往往是一个匿名函数,也就是说Lambda 是一个抽象概念,而编程语言提供了配套支持,比如在 Java 中其实为Lambda 进行配套的就是函数式接口,通过函数式接口生成匿名类和方法进行Lambda 式的处理。

Java Lambda表达式

句法

  • 零参数: () -> System.out.println("零参数 lambda");
  • 一个参数: p -> System.out.println("一个参数:" + p);
  • 多个参数: (p1 [,p2,p3,....pn]) -> System.out.println("多个参数:" + p1 + ", " + p2 + ... + pn);

上面的表达式有一定的限制。它们要么返回一个值要么执行一段方法,并且它们不能包含变量、赋值或语句,例如if or for 。为了进行更复杂的操作,可以使用带有花括号的代码块。如果 lambda 表达式需要返回一个值,那么代码块应该有一个return语句。

(parameter1, parameter2) -> { code block [return] }

方法引用

  • 类 :: 静态方法
Function<String, Integer> function = r -> {
	return Integer.valueOf(r);
};

Function<String, Integer> function = Integer::valueOf;
  • 对象 :: 实例方法
List<String> list = Lists.newArrayList();
Consumer<String> consumer = r -> list.add(r);

Consumer<String> consumer = list::add;
  • 构造器 :: new
Supplier<Object> supplier = () -> new Object();

Supplier<Object> supplier = Object::new;

Stream表达式

Stream,就是JDK8又依托于函数式编程特性为集合类库做的一个类库,它其实就是jdk提供的函数式接口的最佳实践。它能让我们通过lambda表达式更简明扼要的以流水线的方式去处理集合内的数据,可以很轻松的完成诸如:过滤、分组、收集、归约这类操作。

Stream的操作大致分为两类

  • 中间型操作
  • 终结型操作

中间型操作就是返回值依旧是Stream类型的方法。api如下: 1683190725755

终结型操作与中间型相反,返回值是非Stream类型的。api如下:

1683190746765

注意:Stream是惰性的,只有在执行终结型操作时,才会执行中间型操作代码。

Stream.of("1", "2", "3")
        .peek(System.out::println) // 不会打印
        .map(Integer::valueOf);

Stream.of("1", "2", "3")
        .peek(System.out::println) // 会
        .map(Integer::valueOf)
        .count();