Java 函数式接口
函数式接口 (Functional Interface) 就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为 lambda 表达式。Java 允许利用 Lambda 表达式创建这些接口的实例。java.util.function 包是 Java 8 增加的一个新技术点 “函数式接口”,此包共有 43 个接口。这些接口是为了使 Lamdba 函数表达式使用的更加简便,当然也可以自己自定义接口来应用于 Lambda 函数表达式。
JDK 1.8 API 包含了很多内建的函数式接口,比如 Comparator 或者 Runnable 接口,这些接口都增加了 @FunctionalInterface
注解以便能用在 Lamdba 上。从 Function 常用函数入口,真正了解一下函数式接口。
Java 8 中函数式接口
接口 | 描述 | 函数签名 | 范例 |
---|---|---|---|
UnaryOperator<T> |
接收 T 对象,返回 T 对象 | T apply(T t) |
String::toLowerCase |
BinaryOprator<T> |
接收两个 T 对象,返回 T 对象 | T apply(T t1, T t2) |
BigInteger::add |
Predicate<T> |
接收 T 对象,返回 boolean | boolean test(T t) |
Collection::isEmpty |
Function<T, R> |
接收 T 对象,返回 R 对象 | R apply(T t) |
Arrays::asList |
Supplier<T> |
提供 T 对象(例如工厂),不接收值 | T get() |
Instant::new |
Consumer<T> |
接收 T 对象,不返回值 | void accept(T t) |
System.out::println |
标注为 @FunctionalInterface
的接口被称为函数式接口,该接口有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。是否是一个函数式接口,需要注意的有以下几点:
- 该注解只能标记在“有且仅有一个抽象方法”的接口上。
- Java 8 接口中的静态方法和默认方法,都不算是抽象方法。
- 接口默认继承 java.lang.Object,所以如果接口显示声明覆盖了 Object 中方法,那么也不算抽象方法。
- 该注解不是必须的,如果一个接口符合 “函数式接口” 定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了
@FunctionInterface
,那么编译器会报错。 - 在一个接口中定义两个自定义的方法,就会产生 Invalid ‘
@FunctionalInterface
’ annotation; FunctionalInterfaceTest is not a functional interface 错误。@FunctionalInterface
public interface Runnable {
public abstract void run();
}
Consumer:消费型接口(
| 函数式接口 | 描述 | | —- | —- | |void accept(T t)
)Consumer<T>
| 提供一个 T 类型的输入参数,不返回执行结果 | |BiConsumer<T, U>
| 提供两个自定义类型的输入参数,不返回执行结果 | |DoubleConsumer
| 提供一个 double 类型的输入参数,不返回执行结果 | |IntConsumer
| 提供一个 int 类型的输入参数,不返回执行结果 | |LongConsumer
| 提供一个 long 类型的输入参数,不返回执行结果 | |ObjDoubleConsumer<T>
| 提供一个 double 类型的输入参数和一个 T 类型的输入参数,不返回执行结果 | |ObjIntConsumer<T>
| 提供一个 int 类型的输入参数和一个 T 类型的输入参数,不返回执行结果 | |ObjLongConsumer<T>
| 提供一个 long 类型的输入参数和一个 T 类型的输入参数,不返回执行结果 |
(1)作用:消费某个对象
(2)使用场景:Iterable 接口的 forEach 方法需要传入 Consumer,大部分集合类都实现了该接口,用于返回 Iterator 对象进行迭代。
(3)主要方法
方法 | 描述 |
---|---|
void accept(T t) |
对给定的参数执行操作 |
default Consumer<T> andThen(Consumer< ? super T> after) |
返回一个组合函数,after 将会在该函数执行之后应用 |
(4)代码示例Consumer<T>
:提供一个 T 类型的输入参数,不返回执行结果
@Test
public void testConsumer() {
// Consumer<T>:accept(T t)
Consumer<String> consumer = System.out::println;
consumer.accept("hello world!"); // hello world!
// Consumer<T>:andThen(Consumer<? super T> after) -> 返回一个组合函数,after将会在该函数执行之后应用
StringBuilder sb = new StringBuilder("Hello ");
Consumer<StringBuilder> consumer_accept = (str) -> str.append("Jack! ");
Consumer<StringBuilder> consumer_andThen = (str) -> str.append("Bob!");
consumer_accept.andThen(consumer_andThen).accept(sb);
System.out.println(sb.toString()); // Hello Jack! Bob!
}
BiConsumer<T, U>
:提供两个自定义类型的输入参数,不返回执行结果
@Test
public void testBiConsumer() {
// BiConsumer<T, U>:accept(T t, U u)
BiConsumer<String, String> biConsumer = (a, b) -> System.out.printf("%s %s!", a, b);
biConsumer.accept("hello", "world"); // hello world!
}
DoubleConsumer
:提供一个 double 类型的输入参数,不返回执行结果
@Test
public void testDoubleConsumer() {
// DoubleConsumer:accept(double value)
DoubleConsumer doubleConsumer = System.out::println;
doubleConsumer.accept(9.12D); // 9.12
}
ObjDoubleConsumer<T>
:提供一个 double 类型的输入参数和一个 T 类型的输入参数,不返回执行结果
@Test
public void testObjDoubleConsumer() {
// ObjDoubleConsumer<T>:accept(T t, double value)
ObjDoubleConsumer<String> stringObjDoubleConsumer = (s, value) -> System.out.println(s + value);
stringObjDoubleConsumer.accept("金额:", 9.12D); // 金额:9.12
}
Predicate:断言型接口(boolean test(T t)
)
函数式接口 | 描述 |
---|---|
Predicate<T> |
提供一个 T 类型的输入参数,返回一个 boolean 类型的结果 |
BiPredicate<T,U> |
提供两个自定义类型的输入参数,返回一个 boolean 类型的结果 |
DoublePredicate |
提供一个 double 类型的输入参数,返回一个 boolean 类型的结果 |
IntPredicate |
提供一个 int 类型的输入参数,返回一个 boolean 类型的结果 |
LongPredicate |
提供一个 long 类型的输入参数,返回一个 boolean 类型的结果 |
(1)作用:判断对象是否符合某个条件
(2)使用场景:ArrayList
的 removeIf(Predicate)
:删除符合条件的元素。如果条件硬编码在 ArrayList 中,它将提供无数的实现,但是如果让调用者传入条件,这样 ArrayList 就可以从复杂和无法猜测的业务中解放出来。
(3)主要方法
方法 | 描述 |
---|---|
boolean test(T t) |
根据给定的参数进行判断 |
Predicate<T> and(Predicate< ? super T> other) |
返回一个组合判断,将 other 以短路并且的方式加入到函数的判断中 |
Predicate<T> or(Predicate< ? super T> other) |
返回一个组合判断,将 other 以短路或的方式加入到函数的判断中 |
Predicate<T> negate() |
将函数的判断取反 |
(4)代码示例Predicate<T>
:提供一个 T 类型的输入参数,返回一个 boolean 类型的结果
@Test
public void testPredicate() {
// Predicate<T>:boolean test(T t)
Predicate<List<String>> listPredicate = Collection::isEmpty;
System.out.println(listPredicate.test(Arrays.asList("Hello", "World"))); // false
// Predicate<T>:boolean test(T t)
Predicate<Integer> predicate = integer -> integer != 0;
System.out.println(predicate.test(10)); // true
// Predicate<T>:Predicate<T> and(Predicate<? super T> other)
predicate = predicate.and(integer -> integer >= 10);
System.out.println(predicate.test(10)); // true
// Predicate<T>:Predicate<T> or(Predicate<? super T> other)
predicate = predicate.or(integer -> integer != 10);
System.out.println(predicate.test(10)); // true
// Predicate<T>:Predicate<T> negate()
predicate = predicate.negate();
System.out.println(predicate.test(10)); // false
}
Function:函数型接口(R apply(T t)
)
函数式接口 | 描述 |
---|---|
Function<T, R> |
提供一个 T 类型的输入参数,返回一个 R 类型的结果 |
BiFunction<T, U, R> |
提供两个自定义类型的输入参数,返回一个 R 类型的结果 |
DoubleFunction<R> |
提供一个 double 类型的输入参数,返回一个 R 类型的结果 |
DoubleToIntFunction |
提供一个 double 类型的输入参数,返回一个 int 类型的结果 |
DoubleToLongFunction |
提供一个 double 类型的输入参数,返回一个 long 类型的结果 |
IntFunction<R> |
提供一个 int 类型的输入参数,返回一个 R 类型的结果 |
IntToDoubleFunction |
提供一个 int 类型的输入参数,返回一个 double 类型的结果 |
IntToLongFunction |
提供一个 int 类型的输入参数,返回一个 long 类型的结果 |
LongFunction<R> |
提供一个 long 类型的输入参数,返回一个 R 类型的结果 |
LongToDoubleFunction |
提供一个 long 类型的输入参数,返回一个 double 类型的结果 |
LongToIntFunction |
提供一个 long 类型的输入参数,返回一个 int 类型的结果 |
ToDoubleBiFunction<T, U> |
提供两个自定义类型的输入参数,返回一个 double 类型的结果 |
ToDoubleFunction<T> |
提供一个 T 类型的输入参数,返回一个 double 类型的结果 |
ToIntBiFunction<T, U> |
提供两个自定义类型的输入参数,返回一个 int 类型的结果 |
ToIntFunction<T> |
提供一个 T 类型的输入参数,返回一个 int 类型的结果 |
ToLongBiFunction<T, U> |
提供两个自定义类型的输入参数,返回一个 long 类型的结果 |
ToLongFunction<T> |
提供一个 T 类型的输入参数,返回一个 long 类型的结果 |
(1)作用:实现一个“一元函数”,即传入一个值经过函数的计算返回另一个值。
(2)使用场景:V HashMap.computeIfAbsent(K , Function<K, V>)
:如果指定的 key 不存在或相关的 value 为 null 时,设置 key 与关联一个计算出的非 null 值,计算出的值为 null 的话什么也不做(不会去删除相应的 key)。如果 key 存在并且对应 value 不为 null 的话什么也不做。
(3)主要方法
方法 | 描述 |
---|---|
R apply(T t) |
将此参数应用到函数中 |
Function<T, V> andThen(Function< ? super R, ? extends V> after) |
返回一个组合函数,该函数结果应用到 after 函数中 |
Function<V, R> compose(Function< ? super V, ? extends T> before) |
返回一个组合函数,首先将入参应用到 before 函数,再将 before 函数结果应用到该函数中 |
(4)代码示例Function<T, R>
:提供一个 T 类型的输入参数,返回一个 R 类型的结果
@Test
public void testFunction() {
// Function<T, R>:R apply(T t)
Function<String[], List<String>> asList = Arrays::asList;
System.out.println(asList.apply(new String[]{"Hello", "World"})); // [Hello, World]
Function<String, String> function = s -> String.format("%s, Jack!", s);
Function<String, String> compose = s -> StringUtils.isEmpty(s) ? "Hello" : s;
Function<String, String> andThen = String::toUpperCase;
String s = function.compose(compose).andThen(andThen).apply("");
System.out.println(s); // HELLO, JACK!
}
Supplier:供给型接口(R apply(T t)
)
函数式接口 | 描述 |
---|---|
Supplier<T> |
不提供输入参数,返回一个 T 类型的结果 |
BooleanSupplier |
不提供输入参数,返回一个 boolean 类型的结果 |
DoubleSupplier |
不提供输入参数,返回一个 double 类型的结果 |
IntSupplier |
不提供输入参数,返回一个 int 类型的结果 |
LongSupplier |
不提供输入参数,返回一个 long 类型的结果 |
(1)作用:创建一个对象(工厂类)
(2)使用场景:Optional.orElseGet(Supplier< ? extends T>)
:当 this 对象为 null,就通过传入 supplier 创建一个 T 返回。
(3)主要方法
方法 | 描述 |
---|---|
T get() |
获取结果值 |
(4)代码示例Supplier<T>
:不提供输入参数,返回一个 T 类型的结果
@Test
public void testSupplier() {
// Supplier<T>:T get();
Supplier<String> supplier = () -> "Hello Jack!";
System.out.println(supplier.get()); // Hello Jack!
}
Operator:操作型接口(T apply(T t)
)
函数式接口 | 描述 |
---|---|
UnaryOperator<T> |
提供一个 T 类型的输入参数,返回一个 T 类型的结果 |
BinaryOperator<T> |
提供两个 T 类型的输入参数,返回一个 T 类型的结果 |
DoubleBinaryOperator |
提供两个 double 类型的输入参数,返回两个 double 类型的结果 |
DoubleUnaryOperator |
提供一个 double 类型的输入参数,返回一个 double 类型的结果 |
IntBinaryOperator |
提供两个 int 类型的输入参数,返回一个 int 类型的结果 |
IntUnaryOperator |
提供一个 int 类型的输入参数,返回一个 int 类型的结果 |
LongBinaryOperator |
提供两个 long 类型的输入参数,返回一个 long 类型的结果 |
LongUnaryOperator |
提供一个 long 类型的输入参数,返回一个 long 类型的结果 |
(1)作用:实现一个”一元函数“,即传入一个值经过函数的计算返回另一个同类型的值。
(2)使用场景:UnaryOperator
继承了 Function,与 Function 作用相同,不过 UnaryOperator
,限定了传入类型和返回类型必需相同。
(3)主要方法
方法 | 描述 |
---|---|
T apply(T t) |
将给定参数应用到函数中 |
Function<T, V> andThen(Function< ? super T, ? extends V> after) |
返回一个组合函数,该函数结果应用到 after 函数中 |
Function<V, T> compose(Function< ? super V, ? extends T> before) |
返回一个组合函数,首先将入参应用到 before 函数,再将 before 函数结果应用到该函数中 |
(4)代码示例UnaryOperator<T>
:提供一个 T 类型的输入参数,返回一个 T 类型的结果
@Test
public void testOperator() {
// UnaryOperator<T>:T apply(T t)
UnaryOperator<String> unaryOperator = String::toUpperCase;
System.out.println(unaryOperator.apply("Hello World!")); // HELLO WORLD!
}
// BinaryOperator<T> :提供两个 T 类型的输入参数,返回一个 T 类型的结果
@Test
public void testBinaryOperator() {
// BinaryOperator<T>:T apply(T t1, T T2);
BinaryOperator<BigInteger> binaryOperator = BigInteger::add;
System.out.println(binaryOperator.apply(BigInteger.ONE, BigInteger.TEN));
}
总结
java.util.function 包已经提供了大量标注的函数接口。只要标准的函数接口能够满足需求,通常应该优先考虑,而不是专门再构建一个新的函数接口。这样会使 API 更加容易学习,通过减少它的概念内容,显著提升互操作性优势,因为许多标准的函数接口都提供了有用的默认方法。