Java 8 函数式接口

本文节译自 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 }

 supplier与consumer

以下转自Java8之函数式编程Supplier接口和Consumer接口

  • Supplier : 顾名思义,这是一个供应商,提供者.就如一个工厂一样.该类的源码如下:

1 package java.util.function;
2  
3 @FunctionalInterface
4 public interface Supplier<T> {
5  
6     T get();
7 }
  • Consumer : 顾名思义,这是一个消费者,该类的源码如下:
 1 package java.util.function;
 2  
 3 import java.util.Objects;
 4  
 5 @FunctionalInterface
 6 public interface Consumer<T> {
 7  
 8     void accept(T t);
 9  
10     default Consumer<T> andThen(Consumer<? super T> after) {
11         Objects.requireNonNull(after);
12         return (T t) -> { accept(t); after.accept(t); };
13     }
14 }

accept方法

        该函数式接口的唯一的抽象方法,接收一个参数,没有返回值.

andThen方法

        在执行完调用者方法后再执行传入参数的方法.

 实例

 1 import java.util.function.Consumer;
 2  
 3 public class ConsumerTest {
 4     public static void main(String[] args) {
 5         Consumer<Integer> consumer = (x) -> {
 6             int num = x * 2;
 7             System.out.println(num);
 8         };
 9         Consumer<Integer> consumer1 = (x) -> {
10             int num = x * 3;
11             System.out.println(num);
12         };
13         consumer.andThen(consumer1).accept(10);
14     }
15 }
输出结果为:
可以看出先将10传入consumer方法执行,然后再将10传入consumer2方法执行.
 

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。

posted @ 2019-11-13 17:37  lllunaticer  阅读(355)  评论(0编辑  收藏  举报