本文节译自 GeeksForGeeks
Java 8 函数式接口
“函数式接口(Functional Interface)”这个名称来源于"函数式编程(Functional Programming)",
我们最常用的面向对象编程(Java)属于命令式编程(Imperative Programming)这种编程范式。常见的编程范式还有逻辑式编程(Logic Programming),函数式编程(Functional Programming)。
函数式编程作为一种编程范式,在科学领域,是一种编写计算机程序数据结构和元素的方式,它把计算过程当做是数学函数的求值,而避免更改状态和可变数据。
函数式编程并非近几年的新技术或新思维,距离它诞生已有大概50多年的时间了。它一直不是主流的编程思维,但在众多的所谓顶级编程高手的科学工作者间,函数式编程是十分盛行的。
什么是函数式编程?简单的回答:一切都是数学函数。函数式编程语言里也可以有对象,但通常这些对象都是恒定不变的 —— 要么是函数参数,要什么是函数返回值。函数式编程语言里没有 for/next 循环,因为这些逻辑意味着有状态的改变。相替代的是,这种循环逻辑在函数式编程语言里是通过递归、把函数当成参数传递的方式实现的。
举个例子:
a = a + 1这段代码在普通成员看来并没有什么问题,但在数学家看来确实不成立的,因为它意味着变量值得改变。
- 函数式编程的第一个特点就是可以把函数作为参数传递给另一个函数,也就是所谓的高阶函数。例如,对数组进行排序,可以传入一个排序函数作为参数:
String[] array = { "orange", "Pear", "Apple" }; Arrays.sort(array, String::compareToIgnoreCase);
- 函数式编程的第二个特点就是可以返回一个函数,这样就可以实现闭包或者惰性计算:
以上两个特点还仅仅是简化了代码。从代码的可维护性上讲,函数式编程最大的好处是引用透明,即函数运行的结果只依赖于输入的参数,而不依赖于外部状态,因此,我们常常说函数式编程没有副作用。
个人粗浅的理解是,函数式接口因为只有一个方法,因此该接口的作用就像一个函数一样,只能对外提供单一功能但确定的计算。
言归正传
函数式接口是这样的一种接口:包含且仅包含一个抽象方法的接口。在Java 8 中,我们可以使用lambada表达式来作为函数式接口的实现。在Java 8中接口可以有默认方法和静态方法,因此一个函数式接口也可以有多个默认方法。
Runnale, ActionListener, Comparable 都是函数式接口。
在Java 8 以前,我们需要使用匿名内部类,或者显式地的实现接口:
1 // Java program to demonstrate functional interface
2
3 class Test
4 {
5 public static void main(String args[])
6 {
7 // create anonymous inner class object
8 new Thread(new Runnable()
9 {
10 @Override
11 public void run()
12 {
13 System.out.println("New thread created");
14 }
15 }).start();
16 }
17 }
Output:
New thread created
从 Java 8 开始,我们可以将lambda表达式赋值给一引用了函数式接口的对象:
// Java program to demonstrate Implementation of
// functional interface using lambda expressions
class Test
{
public static void main(String args[])
{
// lambda expression to create the object
new Thread(()->
{System.out.println("New thread created");}).start();
}
}
@FunctionalInterface 注解
@FunctionalInterface 注解可以加到函数式接口的定义上方,用来强制检查所声明的接口是函数式接口,只包含了一个抽象方法。如果在有@FunctionalInterface注解的接口中声明了超过一个的抽象方法,编译器会抛出 ‘Unexpected @FunctionalInterface annotation’ 异常。
1 // Java program to demonstrate lamda expressions to implement
2 // a user defined functional interface.
3
4 @FunctionalInterface
5 interface Square
6 {
7 int calculate(int x);
8 }
9
10 class Test
11 {
12 public static void main(String args[])
13 {
14 int a = 5;
15
16 // lambda expression to define the calculate method
17 Square s = (int x)->x*x;
18
19 // parameter passed and return type must be
20 // same as defined in the prototype
21 int ans = s.calculate(a);
22 System.out.println(ans);
23 }
24 }
25 Output:
26
27 25
java.util.function 包
Java 8的java.util.function包里面有很多内置的函数式接口:
- Predicate : Predicate 接口包含一个抽象方法test, test方法会对输入的参数做一个评判,输出一个布尔值作为评判结果,至于评判的criteria交给具体的实现去做。Predicate的函数原型如下:
1 public interface Predicate
2 {
3 public boolean test(T t);
4 }
- BinaryOperator : 见名知意,BinaryOperator 是一个二元输入的函数式接口,它包含一个apply抽象方法,接收两个相同类型的参数,返回一个与输入类型相同的结果。
1 public interface BinaryOperator
2 {
3 public T apply(T x, T y);
4 }
- Function : Function 接口有一个抽象方法apply, 接收一个类型T的输入,返回一个类型R的结果。这个名字也很体现用途,Function 是函数的意思,函数的作用就是将定义域映射到值域的操作。这里T就是定义域,R是值域。映射关系交给具体实现去定义。
1 public interface Function
2 {
3 public R apply(T t);
4 }
示例程序
1 // A simple program to demonstrate the use
2 // of predicate interface
3 import java.util.*;
4 import java.util.function.Predicate;
5
6 class Test
7 {
8 public static void main(String args[])
9 {
10
11 // create a list of strings
12 List<String> names =
13 Arrays.asList("Geek","GeeksQuiz","g1","QA","Geek2");
14
15 // declare the predicate type as string and use
16 // lambda expression to create object
17 Predicate<String> p = (s)->s.startsWith("G");
18
19 // Iterate through the list
20 for (String st:names)
21 {
22 // call the test method
23 if (p.test(st))
24 System.out.println(st);
25 }
26 }
27 }
JDK 1.8 之前已有的函数式接口
- java.lang.Runnable
- java.util.concurrent.Callable
- java.security.PrivilegedAction
- java.util.Comparator
- java.io.FileFilter
- java.nio.file.PathMatcher
- java.lang.reflect.InvocationHandler
- java.beans.PropertyChangeListener
- java.awt.event.ActionListener
- javax.swing.event.ChangeListener
JDK 1.8 新增加的函数接口
- java.util.function
java.util.function 它包含了很多类,用来支持 Java的 函数式编程,该包中的函数式接口有:
| 序号 | 接口 & 描述 |
|---|---|
| 1 | BiConsumer<T,U>
代表了一个接受两个输入参数的操作,并且不返回任何结果 |
| 2 | BiFunction<T,U,R>
代表了一个接受两个输入参数的方法,并且返回一个结果 |
| 3 | BinaryOperator<T>
代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果 |
| 4 | BiPredicate<T,U>
代表了一个两个参数的boolean值方法 |
| 5 | BooleanSupplier
代表了boolean值结果的提供方 |
| 6 | Consumer<T>
代表了接受一个输入参数并且无返回的操作 |
| 7 | DoubleBinaryOperator
代表了作用于两个double值操作符的操作,并且返回了一个double值的结果。 |
| 8 | DoubleConsumer
代表一个接受double值参数的操作,并且不返回结果。 |
| 9 | DoubleFunction<R>
代表接受一个double值参数的方法,并且返回结果 |
| 10 | DoublePredicate
代表一个拥有double值参数的boolean值方法 |
| 11 | DoubleSupplier
代表一个double值结构的提供方 |
| 12 | DoubleToIntFunction
接受一个double类型输入,返回一个int类型结果。 |
| 13 | DoubleToLongFunction
接受一个double类型输入,返回一个long类型结果 |
| 14 | DoubleUnaryOperator
接受一个参数同为类型double,返回值类型也为double 。 |
| 15 | Function<T,R>
接受一个输入参数,返回一个结果。 |
| 16 | IntBinaryOperator
接受两个参数同为类型int,返回值类型也为int 。 |
| 17 | IntConsumer
接受一个int类型的输入参数,无返回值 。 |
| 18 | IntFunction<R>
接受一个int类型输入参数,返回一个结果 。 |
| 19 | IntPredicate
:接受一个int输入参数,返回一个布尔值的结果。 |
| 20 | IntSupplier
无参数,返回一个int类型结果。 |
| 21 | IntToDoubleFunction
接受一个int类型输入,返回一个double类型结果 。 |
| 22 | IntToLongFunction
接受一个int类型输入,返回一个long类型结果。 |
| 23 | IntUnaryOperator
接受一个参数同为类型int,返回值类型也为int 。 |
| 24 | LongBinaryOperator
接受两个参数同为类型long,返回值类型也为long。 |
| 25 | LongConsumer
接受一个long类型的输入参数,无返回值。 |
| 26 | LongFunction<R>
接受一个long类型输入参数,返回一个结果。 |
| 27 | LongPredicate
R接受一个long输入参数,返回一个布尔值类型结果。 |
| 28 | LongSupplier
无参数,返回一个结果long类型的值。 |
| 29 | LongToDoubleFunction
接受一个long类型输入,返回一个double类型结果。 |
| 30 | LongToIntFunction
接受一个long类型输入,返回一个int类型结果。 |
| 31 | LongUnaryOperator
接受一个参数同为类型long,返回值类型也为long。 |
| 32 | ObjDoubleConsumer<T>
接受一个object类型和一个double类型的输入参数,无返回值。 |
| 33 | ObjIntConsumer<T>
接受一个object类型和一个int类型的输入参数,无返回值。 |
| 34 | ObjLongConsumer<T>
接受一个object类型和一个long类型的输入参数,无返回值。 |
| 35 | Predicate<T>
接受一个输入参数,返回一个布尔值结果。 |
| 36 | Supplier<T>
无参数,返回一个结果。 |
| 37 | ToDoubleBiFunction<T,U>
接受两个输入参数,返回一个double类型结果 |
| 38 | ToDoubleFunction<T>
接受一个输入参数,返回一个double类型结果 |
| 39 | ToIntBiFunction<T,U>
接受两个输入参数,返回一个int类型结果。 |
| 40 | ToIntFunction<T>
接受一个输入参数,返回一个int类型结果。 |
| 41 | ToLongBiFunction<T,U>
接受两个输入参数,返回一个long类型结果。 |
| 42 | ToLongFunction<T>
接受一个输入参数,返回一个long类型结果。 |
| 43 | UnaryOperator<T>
接受一个参数为类型T,返回值类型也为T。 |