Java函数式编程教程(一):序章,函数式编程来了

翻译:GentlemanTsao, 2020-06-23


Java函数式编程是指Java中的函数式编程。 从历史上看,用Java编写函数式编程并非易事,函数式编程的某些方面在Java中甚至不可能实现。 在Java 8中,Oracle致力于使函数式编程更容易,并且在一定程度上取得了成功。 在本Java函数式编程教程中,我将介绍函数式编程的基础知识,以及Java中可以实现的部分。

函数式编程基础知识

函数式编程包含以下关键概念:

  • 函数作为第一等对象
  • 纯函数
  • 高阶函数

纯函数式编程也有一组规则要遵循:

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

这些概念和规则将在本教程接下来的部分中进行解释。

即使没有一直遵循所有的这些规则,你仍然可以使应用程序从函数式编程思想中受益。 你会发现,函数式编程并不是万金油。 特别是“无副作用”的想法使它很难实现诸如写入数据库的操作(这是副作用)。 你需要了解函数式编程最擅长解决哪些问题,而不擅长哪些。

函数作为第一等对象

在函数式编程范例中,函数是语言中的第一等对象。 这意味着你可以创建函数的“实例”,就像变量引用该函数实例一样,就像对String,Map或任何其他对象的引用一样。 函数也可以作为参数传递给其他函数。

在Java中,函数不是第一等对象。 与之最接近的是Java Lambda表达式。

纯函数

函数是纯函数的条件:

  • 该函数的执行没有副作用。
  • 函数的返回值仅取决于传递给函数的输入参数。

这是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 <T> IFactory<T> createFactory(IProducer<T> producer, IConfigurator<T> configurator) {
        return () -> {
           T instance = producer.produce();
           configurator.configure(instance);
           return instance;
        }
    }
}

请注意,createFactory()方法返回lambda表达式作为结果。 这是高阶函数的第一个条件。

还要注意,createFactory()方法将两个实例作为参数,它们都是接口的实现(IProducer和IConfigurator)。 Java lambda表达式必须实现功能接口,还记得吗?

设想接口看起来像这样:

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

如你所见,所有这些接口都是函数式接口。 因此它们可以通过Java lambda表达式实现。因此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;
    }
}

此函数明显违反了无状态规则。

无副作用

函数式编程范例中的另一个规则是没有副作用。 指的是函数无法更改函数外部的任何状态。更改函数外部的状态称为副作用。

函数外部的状态既指函数中的类或对象的成员变量,也指函数内部参数中的成员变量,或指外部系统(如文件系统或数据库)中的状态。

不可变变量

功能编程范例中的第三条规则是不可变变量。 不可变变量更容易避免副作用。

递归优先于循环

函数式编程范例中的第四个规则是优先使用递归而非循环。 递归使用函数调用来实现循环,因此代码变得更函数式。

循环的另一种替代方法是Java Streams 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函数式编程教程(二):Java高阶函数

猜你喜欢

转载自blog.csdn.net/GentelmanTsao/article/details/106904821