Java 函数式编程

从历史上看,用 Java 进行函数式编程并不容易,甚至一些函数式编程在 Java 中是不可能实现的。在 Java 8 , Oracle 做了一些努力使得函数式编程变得更容易,这些努力在某种程度上取得了成功。在这个 Java 函数式编程教程中将介绍基本的函数式编程,以及 Java 中可以实现的部分。

函数式编程基础

函数式编程包括以下几个关键的概念:

  • 函数是第一类对象
  • 纯函数
  • 高阶函数

纯函数编程也有一套规则需要遵守:

  • 无状态
  • 无副作用
  • 不可变变量
  • 优先使用递归而不是循环

这些概念和规则将会在这篇教程的剩余部分一一介绍。

尽管如果你一直没有遵守这些规则,你同样可以从函数式编程中获取收获并应用在你的应用中。你将会看到函数式编程并不是解决所有问题的正确工具,特别是 “无副作用” 使得写入数据库(这是个副作用)变得很困难。你需要了解哪些问题函数式编程擅长解决,哪些不是。

函数是第一类对象

在函数式编程范式中,函数是语言里第一类对象。这意味着你可以创建一个函数的 “实例”,作为一个函数实例的引用,就像 String、 Map 和其它对象的引用一样。函数同样可以当做参数传递给其它函数。
在 Java 里,方法 不是第一类对象。 最接近的是 Java Lamda 表达式,在这里我不讨论这个。

纯函数

一个函数如果是一个 纯函数,需要满足以下条件:

  • 函数的执行不产生副作用
  • 函数的返回值只依赖入参

下面是一个 Java 里的 纯函数 例子:

public class ObjectWithPureFunction{

    public int sum(int a, int b) {
        return a + b;
    }
}

注意 sum() 函数返回值只依赖入参。同样注意下 sum() 函数没有副作用,意味着它不改变当前函数以外任何状态(变量)。

肯定的是,下面这是不是一个 纯函数:

public class ObjectWithNonPureFunction{
    private int value = 0;

    public int add(int nextValue) {
        this.value += nextValue;
        return this.value;
    }
}

注意 add() 方法用了一个成员变量来计算它的返回值,而且它也修改改了 value 成员变量的值,因此他有副作用。

高阶函数

一个函数如果是高阶函数则至少要满足以下条件之一:

  • 使用一个或多个函数作为参数
  • 函数返回另外一个函数作为结果

在 Java 中,最接近高阶函数的是:一个函数(方法)使用一个或多个 lambda 表达式作为参数,以及返回另外一个 lambda 表达式。下面就是一个 Java 里 高阶函数 的例子:

public class HigherOrderFunctionClass {

    public  IFactory createFactory(IProducer producer, IConfigurator configurator) {
        return () -> {
           T instance = producer.produce();
           configurator.configure(instance);
           return instance;
        }
    }
}

注意 :1. createFactory() 方法返回一个 lambda 表达式作为返回值。这是作为 函数式编程的一个条件。
2. createFactory() 方法使用两个接口(IProducer and Iconfigurator)的实现的实例作为参数. Java lambda 表达式需要实现一个 函数式接口,记得吗?

想象一下接口是这样:

public interface IFactory {
   T create();
}
public interface IProducer {
   T produce();
}
public interface IConfigurator {
   void configure(T t);
}

你可以看到,这些接口都是函数式接口。因此他们可以被 Java lamda 表达式所实现 —— 所以 createFactory 方法是一个 高阶函数

无状态

像在教程开头提到的,函数式编程范式的规则之一就是无状态。 "无状态" 通常指函数之外没有状态。一个函数可以拥有包含内部临时状态的本地变量,但是这个函数不能引用任何该函数所属类或对象的成员变量。

下面是一个没有使用外部状态的例子:

public class Calculator {
    public int sum(int a, int b) {
       return a + b;
    }
}

相反, 下面是一个使用外部状态的例子

public class Calculator {
    private int initVal = 5;
    public int sum(int a) {
       return initVal + a;
    }
}

这个函数违背了 "无状态" 规则

无副作用

函数式范式另外一个规则就是 "无副作用"。这意味着,一个函数不能修改任何函数之外的状态。改变函数之外的状态被称作一个 副作用
函数外部状态有:函数所属类或对象的成员变量,函数参数中的成员变量,或者外部系统的状态比如文件系统或者数据库。

不可变变量(Immutable Variables)

函数式编程范式的第三个规则是 “不可变变量”。不可变变量有利于防止产生副作用。

优先使用递归而不是循环

函数式编程范式的第四个规则是“优先使用递归而不是循环”。递归使用函数调用来达到循环的目的,因此代码变得更函数式。
循环的另一个代替是 Java Stream API. 这个 API 是受 函数式启发的。

函数式接口

函数式接口在 Java 里是只拥有一个抽象方法的接口。一个抽象方法意味着只有一个方法没有实现。一个接口可以有多个方法比如默认方法和静态方法——两个都有实现,但只要这个接口只有一个没有实现的接口,这个接口就叫做 函数式接口
下面是一个函数式的例子:

public interface MyInterface {
    public void run();
}

下面是另外一个携带默认方法和静态方法实现的函数式接口:

public interface MyInterface2 {
    public void run();

    public default void doIt() {
        System.out.println("doing it");
    }

    public static void doItStatically() {
        System.out.println("doing it statically");
    }
}

注意这两个拥有实现的方法。这仍是一个 函数接口,因为只有 run() 方法没有被实现。可是如果再有一个没有实现的方法,这个接口就不再是函数式接口,不能被 Java lambda 表达式所实现。
译自: Java Functional Programming

你可能感兴趣的:(Java 函数式编程)