一、函数式编程的概念
函数式编程是一种编程范式,它将计算机的运算视为函数的运算,避免变化状态和可变数据,要保持独立,不修改变量。函数式编程是一种声明式编程范式,也就是说,编程使用表达式或声明而不是语句来完成的。
更为简单的一种理解,就是将函数作为一种参数进行传递,也可以将函数作为返回值返回,向上传递。
举个例子,现在有如下运算:
(10 + 5) * 2 - 8
按照传统的过程式编程,应该为:
int a = 10 + 5;
int b = a * 2;
int c = b - 8;
而函数式编程要求使用函数,我们把不同的运算转换为函数,写成如下样式:
int result = subtract(multiply(add(10,5), 2), 8);
这就是函数式编程。
函数式编程的自由度很高,可以写出很接近自然语言的代码。我们将上述代码变形,就可以得到下面的结果:
add(10,5).multiply(2).subtract(8);
这基本就是自然语言的表达了,大家应该一眼就能明白它的意思吧.
二、Lambda表达式
函数式编程最重要的基础是Lambda表达式。Lambda 表达式是Java 8 的重要特性。
Lambda 表达式把函数作为一个方法的参数(函数作为参数传递进方法中)。Lambda表达式的基本写法如下:
(parameters) ->{ statements; }
其中,箭头的左边是参数列表,右边是函数体。
在Lambda表达式中,方法的引用为:
class::method
其中,左侧是类或对象,右侧是方法。
例如,静态方法的引用:
Math::pow
实例方法的引用:
System.out::println
构造方法的引用:
Person::new
如果有方法重载的情况,编译器会通过调用者的参数类型和参数数量找出你想要的那个方法。
lambda表达式的特征:
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
- 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
public class People {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public People(String name){
this.name = name;
}
}
public class PeopleTest {
public static void main(String[] args) {
List<String> nameList = new ArrayList<>();//假设有值
...
//这里用到了一些stream流式处理的知识,我们后续发文讲解
List<People> peopleList = nameList.stream().map(People::new).collect(Collectors.toList());
nameList.forEach(name -> System.out.println(name));
peopleList.sort((o1, o2) -> o1.getName().compareTo(o2.getName()));
}
}
Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。
三、函数式编程的特点
函数的执行没有副作用,不会带来状态的改变,即不会修改函数外部的变量,函数的返回值仅仅依赖于输入参数。
函数参数可以是一个,也可以是多个,函数的返回值也可以是一个函数。
四、函数式接口
有且仅有一个抽象方法的接口叫做函数式接口。函数式接口可以被隐式转换为 lambda 表达式。
有两种方法可以声明函数式接口。一种是在接口上加上@FunctionalInterface注解,将接口声明为函数式接口,此时的接口只能拥有一个抽象方法,添加第二个抽象方法时会报错。另一种是在接口中只保留一个抽象方法,此时接口被隐式地加上了@FunctionalInterface注解,不同的是此时的接口是可以新增抽象方法的,但是一旦添加了多于一个的抽象方法,该接口就不是函数式接口了。
Java中有几个内建的函数式接口:
a接口名 | 方法 | 含义 |
---|---|---|
Function<T,R> | R apply(T t); | 表示一个函数,它接受一个单一的参数并返回一个单一的值 |
Predicate<T> | boolean test(T t); | 表示一个函数,它只取一个值作为参数,并返回真或假 |
Supplier<T> | T get(); | 表示一个函数,它提供了某种类型的值 |
Consumer<T> | void accept(T t); | 表示一个函数,它接受一个参数但不返回任何值 |
UnaryOperator<T> | T apply(T t); | 继承自Function<T,T>,接受一个类型的参数,返回一个相同类型的值 |
BiFunction<T,U,R> | R apply(T t, U u); | 增强型的Function,接收两个不同类型的参数,返回一个第三种类型的值 |
BinaryOperator<T> | T apply(T t1, T T2); | 继承自BiFunction<T,T,T>,接收两个相同类型的参数,返回一个相同类型的值 |
以上是一些在Java函数式编程中常用的几种函数式接口,它们的使用其实是实现了接口的抽象方法,然后以参数的形式传入相关的方法,而且都可以转化为Lambda表达式的方式。
下面是几个例子,方便大家理解。
public class FunctionalInterfaceTest {
/**
* 接收一个值,经过一系列操作后返回另一个值(返回值类型自定义)
*/
public static void functionTest(){
Function<Integer,Integer> function = (value) -> value + 2;
Integer result = function.apply(10);
System.out.println(result);
}
/**
* 接收一个值,进行运算,返回true\false
*/
public static void predicateTest(){
Predicate<Integer> predicate = (value) -> value > 0;
Boolean result = predicate.test(10);
System.out.println(result);
}
/**
* 提供一个方法,该方法仅做生成值用
*/
public static void supplierTest(){
Supplier<Integer> supplier = () -> (int)(Math.random() * 10000);
Integer result = supplier.get();
System.out.println(result);
}
/**
* 接收一个值,进行一系列运算,不返回任何值
*/
public static void consumerTest(){
Consumer<Integer> consumer = (value) -> System.out.println(value);
consumer.accept(100);
}
public static void unaryOperatorTest(){
UnaryOperator<Integer> unaryOperator = (value) -> value += 10;
Integer result = unaryOperator.apply(10);
System.out.println(result);
}
public static void binaryOperatorTest(){
BinaryOperator<Integer> binaryOperator = (a,b) -> a + b;
Integer result = binaryOperator.apply(10,20);
System.out.println(result);
}
/**
* 增强型Function
*/
public static void biFunctionTest(){
BiFunction<Integer,Integer,Integer> biFunction = (a,b) -> a * b + 100;
Integer result = biFunction.apply(100,200);
System.out.println(result);
}
public static void main(String[] args) {
functionTest();
predicateTest();
supplierTest();
consumerTest();
unaryOperatorTest();
binaryOperatorTest();
biFunctionTest();
}
}
结果如下:
12
true
9782
100
20
30
20100
五、函数的组合
我们在开发过程中,一个函数式接口往往是不能满足我们的需求。比如开头的那个数学表达式,( 10 + 5 )* 2 – 8至少需要三个函数。如果我们能够事先将几个函数组合成一个函数,是不是就可以预制好我们想要的运算,只需要调用者传入想要计算的参数,就可以得到结果了呢?
Java提供了四个方法,方便开发者组合函数:
方法名 | 含义 |
---|---|
Predicate and(Predicate other); | and方法,将调用方与参数两者做与运算,返回一个Predicate对象。此方法仅提供给Predicate使用。 |
Predicate or(Predicate other); | or方法,将调用方与参数两者做或运算,返回一个Predicate对象。此方法仅提供给Predicate使用。 |
Function compose(Function before); | compose方法,将调用者和参数组合在一起进行运算,先执行参数的运算,后执行调用者的运算。此方法仅提供给Function使用。 |
Function andThen(Function after); | andThen方法,将调用者和参数组合在一起进行运算,先执行执行调用者的运算,后参数的运算。此方法提供给Function和Consumer使用。 |
我们来看一下如何使用:
public class FunctionalInterfaceTest {
/**
* 按顺序执行function
*/
public static void functionAndThenTest(){
Function<Integer,Integer> function1 = (value) -> value + 2;
Function<Integer,Integer> function2 = (value) -> value * 10;
Function<Integer,Integer> function3 = function1.andThen(function2);
Integer result = function3.apply(100);
System.out.println(result);
}
/**
* 先计算参数内的
*/
public static void functionComposeTest(){
Function<Integer,Integer> function1 = (value) -> value + 2;
Function<Integer,Integer> function2 = (value) -> value * 10;
Function<Integer,Integer> function3 = function1.compose(function2);
Integer result = function3.apply(100);
System.out.println(result);
}
public static void predicateAndTest(){
Predicate<Integer> predicate1 = (value) -> value > 0;
Predicate<Integer> predicate2 = (value) -> value >= 10;
Predicate<Integer> predicate3 = predicate1.and(predicate2);
Boolean result = predicate3.test(5);
System.out.println(result);
}
public static void predicateOrTest(){
Predicate<Integer> predicate1 = (value) -> value > 0;
Predicate<Integer> predicate2 = (value) -> value >= 10;
Predicate<Integer> predicate3 = predicate1.or(predicate2);
Boolean result = predicate3.test(5);
System.out.println(result);
}
public static void consumerAndThenTest(){
Consumer<Integer> consumer1 = (value) -> System.out.println(value);
Consumer<Integer> consumer2 = (value) -> System.out.println(value * value);
Consumer<Integer> consumer3 = consumer1.andThen(consumer2);
consumer3.accept(100);
}
public static void main(String[] args) {
functionAndThenTest();
functionComposeTest();
predicateAndTest();
predicateOrTest();
consumerAndThenTest();
}
}
执行结果:
1020
1002
false
true
100
10000
函数式编程使得Java语言更加接近自然语言,易于理解,而且开发迅速,代码简洁。函数式编程不依赖、不改变状态外部,只与输入参数有关系,每一个函数都可以看做是一个独立单元,方便管理,有利于进行单元测试,模块化管理。函数式编程作为Java语言的一种高级特性,已经获得广泛的应用,比如Java中的流式计算,在实战开发中,也经常用到。
没有回复内容