大部分程序员更习惯命令式(imperative)的编程风格,既要告诉程序做什么
,也要说明怎么做
?
例如:
public static void doSomething(List<String> names) {
boolean found = false;
for(String name : names) {
if(name.equals("AAA")) {
found = true;
break;
}
}
if(found)
System.out.println("Do something if found");
else
System.out.println("Do nothing if not found");
}
}
上面就是一个典型的命令式程序,程序中既有如何查找目标对象的逻辑,又有找到后做什么的逻辑!
结果就造成代码拖沓、冗长、低效!
声明式(declarative),顾名思义就是声明做什么
,不关心、也不负责怎么做
。
例如:
public static void doSomething(List<String> names) {
if(names.contains("AAA"))
System.out.println("Do something if found");
else
System.out.println("Do nothing if not found");
}
}
怎么做
的问题交由 contains
解决!
函数式(functional)编程风格是一种更高效、更简洁的声明式风格!
高阶函数(higher order function)是指能够接受(函数参数)、创建(函数对象)、或返回函数的函数!在 JavaScript 或者 Python 中,函数就是对象,是一等公民。而 Java 有所不同,Java 使用 functional interfaces 来创建函数对象(或者 lambda 表达式)。
例如:
public class Demo{
interface Action{
public void doSomething();
}
public static void demoFunctional() {
receiveFunction(returnFunction()); // 输出 Demo higher order function!
}
// 接收 Function
public static void receiveFunction(Action action){
action.doSomething();
}
// 返回 Function
public static Action returnFunction(){
return () -> System.out.println("Demo higher order function!");
}
public static void main(String[] args){
demoFunctional();
}
}
在 Java 中进行函数式编程需要使用函数接口!
示例:
interface Runnable {
void run();
}
函数接口(functional interface)需要满足以下特点:
下面的接口不是函数式接口:
// 方法名不能是 equals
interface NonFunc {
boolean equals(Object obj);
}
// 不能有两个抽象方法
interface Foo {
int m();
Object clone();
}
例如,下面的函数接口为 JDK8 Built-in 接口,apply 为抽象方法, compose, andThen 为 default 方法,identity 为 static 方法:
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
其中两个默认方法本身就是高阶函数,因为它们能接收函数并返回新的函数。
示例代码:
public class Demo
{
public static void main( String[] args )
{
Function<String, Integer> caller = (s) -> Integer.parseInt(str);
Function<Integer, String> callee = (i) -> Integer.toString(i + 5);
Function<String, String> andThenFun = caller.andThen(callee);
Function<Integer, Integer> composeFun = caller.compose(callee);
System.out.println(andThenFun.apply("10"));
System.out.println(composeFun.apply(10));
}
}
首先要清楚函数的执行顺序,然后是匹配输入和输出的参数类型。
Java 是静态类型语言,因此在进行函数式编程时肯定不如 python 或 js 那样自由、灵动,类型匹配绕来绕去,显得啰里啰嗦。
Function
Steam 的 map 方法能接收 Function
public class Demo{
public static void main( String[] args )
{
List<Integer> list = Arrays.asList(1,2,3,4,5,6);
list.stream().map(i -> Integer.toString(i))
.forEach(s -> System.out.println(String.format("class: %s, value: %s", s.getClass(), s)));
}
}
Function
同理,Stream 中的 filter 接收 Predicate 函数对象,forEach 接收 Comsumer 对象。
尽管 JDK 已经提供了足够丰富的函数接口,但有时仍然需要定制一些函数接口。
创建函数接口:
@FunctionalInterface
public interface Transformer<T> {
T transform(T input);
}