翻译:GentlemanTsao, 2020-06-23
函数式编程包含以下关键概念:
纯函数式编程也有一组规则要遵循:
这些概念和规则将在本教程接下来的部分中进行解释。
即使没有一直遵循所有的这些规则,你仍然可以使应用程序从函数式编程思想中受益。 你会发现,函数式编程并不是万金油。 特别是“无副作用”的想法使它很难实现诸如写入数据库的操作(这是副作用)。 你需要了解函数式编程最擅长解决哪些问题,而不擅长哪些。
在函数式编程范例中,函数是语言中的第一等对象。 这意味着你可以创建函数的“实例”,就像变量引用该函数实例一样,就像对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高阶函数