0%

stream and lambda(3) - jdk 提供的函数式接口

使用函数式接口开发,如果针对每个需要使用的场景都自定义一个函数式接口,会导致我们的项目中有很多仅仅当参数的接口。如果我们能自己抽离这些使用场景的共性,则会省去很多自定义函数式接口,相信很多人会遇到这种情况。幸运的是,JDK 已经提前帮我们把这一步做好了。

Java8 之前的函数式接口

Java8 之前是没有函数式接口这个概念的,但是在 Java8 中,有些接口升级成了函数式接口。最常见的当属以下三位选手:

1 . java.lang.Runnable
2 . java.util.concurrent.Callable
3 . java.util.Comparator

Java8 之后的函数式接口

Java8 提供了一系列的函数式接口,主要在 java.util.function 包目录下。在了解这些函数式接口前,再默念几遍之前反复提到的要点:接口名不重要(反正也是匿名),方法名不重要(反正只有一个抽象方法),重要的是能干什么(行为)

接口入参返回类型说明
Function<T,R>TR输入 T 返回 R,有输入有输出
Consumer<T>T/纯消费,只有输入没有输出
Supplier<T>/T纯供应,没有输入只有输出
Predicate<T>Tboolean断言,输出布尔类型
UnaryOperator<T>TT一元函数,输入输出类型相同
BinaryOperator<T>(T,T)T二元函数,输入输出类型相同

Function<T,R>

使用说明:当铺,用东西换钱。

先看源码

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
@FunctionalInterface
public interface Function<T, R> {

// 入参类型为T,处理后,返回类型为R的结果。
R apply(T t);

// 入参为 Function 对象 before,返回一个 Function 对象 result。
// 执行时,先执行一次 before.apply 方法的,再执行 result.apply
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}

// 入参为 Function 对象 after,返回一个 Function 对象 result。
// 执行时,先执行一次 result.apply 方法的,再执行 after.apply
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}

// 静态方法,返回一个只会返回入参的 Function 对象。
// 相当于 t->t 的简写,主要用于返回 lambda 表达式
static <T> Function<T, T> identity() {
return t -> t;
}
}

实践

apply 方法,处理传入的参数并返回。

1
2
3
4
public void doubleNum() {
Function<Integer, String> d = integer -> String.valueOf(2 * integer);
System.out.println(d.apply(10));
}

compose 方法,是传入一个前置处理的函数,前置函数的返回结果是 apply 的入参。

1
2
3
4
5
6
7
8
9
10
11
12
public void compose() {
Function<Integer, String> d = integer -> {
System.out.println("real double apply begins, param is " + integer);
return String.valueOf(2 * integer);
};
//先将输入的字符串转为Integer,再乘法。compose的入参可以指定,输出一定要是apply的入参
Function<String, String> compose = d.compose(s -> {
System.out.println("compose begins, str to integer, str is " + s);
return Integer.valueOf(s);
});
System.out.println("final result is " + compose.apply("10"));
}

andThen 方法,传入一个后置函数,可以形成链式处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void andThen() {
Function<Integer, String> d = integer -> {
System.out.println("real double apply begins, param is " + integer);
return String.valueOf(2 * integer);
};

//andThen传入的是apply的结果,处理后的类型可以指定
Function<Integer, String> andThen = d.andThen(s -> {
System.out.println("and then begins");
return "after and then, result is " + s;
});

System.out.println(andThen.apply(10));
}

identity,是 t -> t的一种替代式写法。

1
2
3
4
5
6
7
8
9
public void identify() {
List<Student> list = new ArrayList<>();
list.add(new Student("1", "张三"));
list.add(new Student("2", "李四"));

//两种写法等效的
Map<String, Student> map1 = list.stream().collect(Collectors.toMap(Student::getId, s -> s));
Map<String, Student> map2 = list.stream().collect(Collectors.toMap(Student::getId, Function.identity()));
}

其他类型 Function

接口函数说明
BiFunctionR apply(T t, U u)二元入参
DoubleFunctionR apply(double value)输入为double
DoubleToIntFunctionint applyAsInt(double value)输入double,输出int
DoubleToLongFunctionlong applyAsLong(double value)输入double,输入long
IntFunctionR apply(int value)输入为int
IntToDoubleFunctiondouble applyAsDouble(int value)输入int,输出double
IntToLongFunctionlong applyAsLong(int value)输入int,输出long
LongFunctionR apply(long value)输入为long
LongToDoubleFunctiondouble applyAsDouble(long value)输入long,输出double
LongToIntFunctionint applyAsInt(long value)输入long,输出int
ToLongFunctionlong applyAsLong(T value)输出为long
ToLongBiFunctionlong applyAsLong(T t, U u)二元入参,输出为long
ToIntFunctionint applyAsInt(T value)输出为int
ToIntBiFunctionint applyAsInt(T t, U u)二元入参,输出为int
ToDoubleFunctiondouble applyAsDouble(T value)输出为long
ToDoubleBiFunctiondouble applyAsDouble(T t, U u)二元入参,输出为long

Consumer<T>

使用说明:貔貅,光进不出。

先看源码

1
2
3
4
5
6
7
8
9
10
11
@FunctionalInterface
public interface Consumer<T> {
//处理逻辑
void accept(T t);

//传入一个后置函数
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}

实践

Consumer 主要是用于提供一个可以对入参处理的函数,比如用在 foreach

1
2
3
4
5
6
7
8
public static void main(String[] args) {
Consumer<String> c1 = str -> System.out.println("this is the first time, word is " + str);
Consumer<String> c2 = str -> System.out.println("this is the second time, word is " + str);
List<String> stringList = Arrays.asList("I", "am", "the", "demo");

stringList.forEach(c1);
stringList.forEach(c1.andThen(c2));
}

其他类型 Consumer

接口函数说明
BiConsumeraccept(T t, U u)二元入参
DoubleConsumervoid accept(double value)入参为double
IntConsumervoid accept(int value)入参为int
LongConsumervoid accept(long value)入参为long
ObjDoubleConsumervoid accept(T t, double value)后参为double
ObjIntConsumervoid accept(T t, int value)后参为int
ObjLongConsumervoid accept(T t, long value)后参为long

Supplier<T>

使用说明:老好人,散财童子,啥都不要。

先看源码

Supplier 的源码就比较简单了,只有一个 get 方法,返回指定类型对象。

1
2
3
4
@FunctionalInterface
public interface Supplier<T> {
T get();
}

实践

1
2
3
4
5
6
7
8
public void demo() {
Supplier<Double> random = Math::random;
System.out.println(random.get());

Supplier<String> s = String::new;
String result = Stream.of("this", "is", "is", "a", "demo").findFirst().orElseGet(s);
System.out.println(result);
}

其他类型 Supplier

接口函数说明
BooleanSupplierboolean getAsBoolean()返回boolean
DoubleSupplierdouble getAsDouble()返回double
IntSupplierint getAsInt()返回int
LongSupplierlong getAsLong()返回long

Predicate<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
@FunctionalInterface
public interface Predicate<T> {

//业务判断逻辑
boolean test(T t);
//and 逻辑
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
//取非逻辑
default Predicate<T> negate() {
return (t) -> !test(t);
}
//or 逻辑
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
//静态方法,返回一个Predicate函数,用于判断两个对象是否相等
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}

实践

Predicate 主要用于有布尔类型的场景。

1
2
3
4
5
6
7
8
9
10
public static void demo() {
Predicate<Integer> p1 = integer -> integer > 10;
Predicate<Integer> p2 = integer -> integer < 5;

System.out.println("7 > 10 :" + p1.test(7));
System.out.println("7 !< 5 :" + p2.negate().test(7));
System.out.println("7 > 10 && 7 < 5 : " + p1.and(p2).test(7));
System.out.println("0 > 10 || 0 < 5 : " + p1.or(p2).test(0));
System.out.println("null equals null : " + Predicate.isEqual(null).test(null));
}

其他类型 Predicate

接口函数说明
BiPredicateboolean test(T t, U u)二元入参
DoublePredicateboolean test(double value)入参double
IntPredicateboolean test(int value)入参返回int
LongPredicateboolean test(long value)入参返回long

UnaryOperator<T>

使用说明:汇率结算,用钱换钱。

先看源码

UnaryOperator 继承 Function,只不过是输入输出类型相同。

1
2
3
4
5
public interface UnaryOperator<T> extends Function<T, T> {
static <T> UnaryOperator<T> identity() {
return t -> t;
}
}

实践

1
2
3
4
5
6
7
public void unaryDemo() {
Function<Integer, Integer> squareFunc = integer -> integer * integer;
UnaryOperator<Integer> squareUnary = integer -> integer * integer;

System.out.println(squareFunc.apply(9));
System.out.println(squareUnary.apply(9));
}

BinaryOperator<T>

使用说明:合成机器,两个宝石合成一个宝石。

先看源码

BinaryOperator 继承自 BiFunction,只不过是两个入参以及返回类型相同。

同时,BinaryOperator 提供了 minBymaxBy 两个静态方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {

//根据比较器,返回较小的那个
public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
}

//根据比较器,返回较大的那个
public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
}
}

实践

比如整数四则运算,两个输入和输出是一样的类型。

1
2
3
4
public void multiply() {
BinaryOperator<Integer> multiply = (i1, i2) -> i1 * i2;
System.out.println(multiply.apply(3, 5));
}

minBymaxBy,按比较器输出。

1
2
3
4
5
6
public void compare() {
BinaryOperator<Integer> min = BinaryOperator.minBy(Comparator.comparingInt(i -> i));
BinaryOperator<Integer> max = BinaryOperator.maxBy(Comparator.comparingInt(i -> i));
System.out.println(min.apply(1,2));
System.out.println(max.apply(1,2));
}

总结

函数式接口,归根究底还是接口,定义的是标准行为。但是函数式接口又只有一个抽象函数,使得我们可以不关注函数名字到底是什么,而专注于业务逻辑。

JDK 提供的这一套函数式接口,全部在 java.util.function 包中,基本上可以满足我们常用场景的使用需求。这些接口有些抽象,但只要将关注点转移到这个接口到底能干什么,就不再难理解了。当然,如果还有特殊的使用场景,可以通过自定义函数式接口解决。

注:本文配套代码可在 github 查看:stream-and-lambda