所谓的函数式编程就是把函数名字当做值进行传递,然后接收方拿到这个函数名进行调用。
首先来看下JavaScript如何进行函数调用
1var Person = { 2 sayHello: function(name) { 3 alert('hello ' + name); 4 } 5}; 6 7function work(fn) { 8 fn('Jim'); 9} 10 11work(Person.sayHello); // hello Jim 复制代码
在这个例子中,sayHello函数被当做一个参数传递到另一个work函数当中,然后work调用给定的函数。
在Java中,充当函数的角色是类中方法,在本篇文章当中提到的函数泛指方法。
接下来看下Java8中一个简单的函数式编程例子:
1import java.util.function.Consumer; 2 3class Person { 4 public static void sayHello(String name) { 5 System.out.println("hello " + name); 6 } 7} 8 9public class TestPerson { 10 11 public static void main(String[] args) { 12 work(Person::sayHello); // hello Jim 13 } 14 15 public static void work(Consumer<String> consumer) { 16 consumer.accept("Jim"); 17 } 18} 复制代码
从这个例子中可以看到,Java8传递函数的方式跟JavaScript不一样,在Java8当中传递函数的方式是:方法引用::方法名称,如:String.valueOf,String.toString。接收参数Consumer可以先不用去了解,后面会讲到。
为了接收传递过来的函数引用,Java设计者提出了一个函数式接口的概念
函数式接口定义:一个接口里面只有一个抽象方法。
如下面几个接口都是函数式接口:
Consumer
Supplier
Runnable
Callable
查看Runnable类的源码可以发现,在类上方定义了一个@FunctionalInterface注解
1@FunctionalInterface 2public interface Runnable { 3 public abstract void run(); 4} 复制代码
当做一个标记使用,标记当前接口是一个函数式接口,如果你的接口有多个抽象方法或没有抽象方法,那么会编译报错。它的作用有点类似于@override注解,有辅助作用,但不是必须的。如果你想设计一个函数式接口,那么最好把它加上。
1// 编译不通过,必须要有一个抽象方法 2@FunctionalInterface 3public interface InterfaceA { 4 5} 6 7// 编译通过,只有一个抽象方法 8@FunctionalInterface 9public interface InterfaceB { 10 void run(); 11} 12 13// 编译不通过,只能有一个抽象方法 14@FunctionalInterface 15public interface InterfaceC { 16 void run(); 17 18 void run2(); 19} 20 21// 编译通过,run2是默认方法,并不是抽象方法 22// 在这里只有一个run抽象方法 23@FunctionalInterface 24public interface InterfaceD { 25 void run(); 26 27 default void run2() {}; 28} 复制代码
在例子InterfaceD中涉及到了接口默认方法default void run2() {};,这里略过不讲,后续文章中会有提到。
我们可以实现一个自定义的函数式接口,替代Consumer
1// 自定义函数式接口 2@FunctionalInterface 3public interface FunctionCaller<T> { 4 void call(T t); 5} 6 7public class TestPerson2 { 8 9 public static void main(String[] args) { 10 work(Person::sayHello); 11 } 12 13 public static void work(FunctionCaller<String> caller) { 14 caller.call("Jim"); 15 } 16} 复制代码
本篇主要讲解了在Java8下如何进行函数式编程,以及如何定义和使用一个函数式接口。