lambda表达式的使用
咕咕咕 fishing

函数式编程

在使用 lambda 之前,要先了解下函数式编程。
函数式编程-wiki

函数式编程,或称函数程序设计、泛函编程(英语:Functional programming),是一种编程范型,它将电脑运算视为函数运算,并且避免使用程序状态以及可变物件。
它的特点就是,函数和其他变量一样,可以作为参数传递给其他函数,也可以作为其他函数的返回值。

可以作为参数,意味着函数嵌套,数学里的高阶函数 f(g(x)) 这种。
可以作为返回值,可以实现惰性计算。(在需要结果时才对其进行计算,避免不必要的运算,节约内存。

简单写下吧,概念性的东西不是我所擅长的。重点就是 函数也可以作为变量

lambda 表达式

Lambda 是 JDK8 中一个语法糖。他可以对某些匿名内部类的写法进行简化。(但是不能完全替代匿名内部类的使用
它是函数式编程思想的一个重要体现。让我们不用关注是什么对象。而是更关注我们对数据进行了什么操作。

下面直接举例子,关于线程的创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Main {

public static void main(String[] args) {

// 使用匿名内部类的方式
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run");
}
}).start();

// 简化匿名内部类的方式
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " run");
}).start();

// lambda 表达式简化
new Thread(() -> System.out.println(Thread.currentThread().getName() + " run")).start();

}
}

使用 lambda 的情况:要求实现的接口是 函数式接口
函数式接口可以被隐式转换为 lambda 表达式。
那么什么是函数式接口?看看上面例子中 Runnable接口 的实现:

1
2
3
4
5
6
7
8
package java.lang;

// 注释...
@FunctionalInterface
public interface Runnable {
// 注释...
public abstract void run();
}

简单的:
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。

具体的:
函数式接口:

  1. 有且仅有一个抽象方法。
  2. 可以有 默认(default)方法,因为有默认实现,不是抽象的。
  3. 接口默认继承java.lang.Object,所以如果接口显示声明覆盖了Object中方法,那么也不算抽象方法。
  4. @FunctionalInterface 不是必须的,如果一个接口符合”函数式接口”定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了 @FunctionInterface,那么编译器会报错。

所以 lambda 表达式主要是对这种函数式接口的简化。
它的主要原则:可推导可省略

因为知道传入参数的类型,所以 new 匿名内部类的步骤可以省略。
因为这个接口只有一个抽象方法需要实现,所以方法名可以省略。
只有一个方法,那么这个方法的参数类型和返回值类型也可以省略。

最后简化的结果就是 (参数) -> {方法体}
都省这么多了,那就再省一点。当参数值有一个时,小括号也可以省略;当方法体只有一条语句时,大括号也可以省略。
就变成了 一个参数 -> 语句

jdk 实现

java.util.function 它包含了很多类,用来支持 Java的 函数式编程。

主要的四个类是:
Function<T, R> 接受一个参数,返回一个结果。
Consumer 接受一个参数,执行一些操作,无返回值。
Supplier 不接受参数,生成一个值,并返回一个值。
Predicate 接受一个参数,返回一个布尔值。

其余基本是这四个的拓展。
Bi前缀为接收两个参数,以及指定特定类型。不像这四个使用的都是泛型。

下面是它们接口的源码,很简单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package java.util.function;

import java.util.Objects;

/**
* Represents a function that accepts one argument and produces a result.
*/
@FunctionalInterface
public interface Function<T, R> {

/**
* Applies this function to the given argument.
*/
R apply(T t);

/**
* Returns a composed function that first applies the {@code before}
* function to its input, and then applies this function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}

/**
* Returns a composed function that first applies this function to
* its input, and then applies the {@code after} function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}

/**
* Returns a function that always returns its input argument.
*/
static <T> Function<T, T> identity() {
return t -> t;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package java.util.function;

import java.util.Objects;

/**
* Represents an operation that accepts a single input argument and returns no
* result. Unlike most other functional interfaces, {@code Consumer} is expected
* to operate via side-effects.
*/
@FunctionalInterface
public interface Consumer<T> {

/**
* Performs this operation on the given argument.
*/
void accept(T t);

/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
package java.util.function;

/**
* Represents a supplier of results.
*/
@FunctionalInterface
public interface Supplier<T> {

/**
* Gets a result.
*/
T get();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package java.util.function;

import java.util.Objects;

/**
* Represents a predicate (boolean-valued function) of one argument.
*/
@FunctionalInterface
public interface Predicate<T> {

/**
* Evaluates this predicate on the given argument.
*/
boolean test(T t);

/**
* Returns a composed predicate that represents a short-circuiting logical
* AND of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code false}, then the {@code other}
* predicate is not evaluated.
*/
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}

/**
* Returns a predicate that represents the logical negation of this
* predicate.
*/
default Predicate<T> negate() {
return (t) -> !test(t);
}

/**
* Returns a composed predicate that represents a short-circuiting logical
* OR of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code true}, then the {@code other}
* predicate is not evaluated.
*/
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}

/**
* Returns a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}.
*/
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}

关于它们默认方法的使用,这里也举一个小例子来说明。
关于 FUnction<T, R> 默认方法 compose 和 andThen 的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Main {
public static void main(String[] args) {
// 使用 lambda 定义 Function 的实现
Function<String, String> f1 = s -> s.toUpperCase();
Function<String, String> f2 = s -> s + " world";
// f1 执行之前 执行 f2
// HELLO WORLD
String res1 = f1.compose(f2).apply("hello");
// f1 执行之后 执行 f2
// HELLO world
String res2 = f1.andThen(f2).apply("hello");

System.out.println(res1);
System.out.println(res2);
}
}

由于这些默认函数的返回值是接口本身,所以可以很愉快的进行链式调用。

关于 stream 流 以及总结

得益于 Lambda 的引入,让 Java 8 中的流式操作成为可能,Java 8 提供了 stream 类用于获取数据流,它专注对数据集合进行各种高效便利操作,提高了编程效率,且同时支持串行和并行的两种模式汇聚计算。能充分的利用多核优势。

流式操作很爽,流式操作一切从这里开始。

1
2
3
4
// 为集合创建串行流
stream()
// 为集合创建并行流
parallelStream()

stream 流应该是重点,且看下回。
lambda 主要是将函数作为参数和返回值。
将计算的过程抽象为一个函数,可以将这个函数作用于其他的数据,也可以让其他函数返回一个函数。
这里写的比较抽象,还是在自己写的时候更容易理解,多写吧。

这篇个人感觉写的不是很好,有些乱了。
因为从 Optional 看到 lambda 再到 stream,想去翻翻源码,然后越看越乱。
发现 stream 流创建时,还分为并行流和串行流。和多线程有关的没一个简单的啊
决定先整理一点,缓一缓。

其他参考

函数式编程-入门篇章
函数式编程之惰性求值
函数式编程的核心思想
Java高级特性—-函数式编程的使用
Java 8 函数式接口

Lambda表达式和匿名内部类的区别
Java 8 Lambda 表达式介绍
Java Lambda 表达式
Java8新特性 Lambda底层实现原理
Java8系列 (一) Lambda表达式
快速看清lambda的本质