Functional Programming 函数式编程,这里的函数类似于一个表达式,类似我们学习中的数学公式,输入端是确定的,那输出端也是确定的,这种就是没有副作用的纯函数。
函数式编程认为,程序可以用一系列数学函数或表达式的组合来表示。函数式编程是程序面向数学的更底层的抽象,将计算过程描述为表达式。抽象程度很高,但是也只是理论上,毕竟不是所有的程序都使用,你太抽象了,反而不可理解,很多我们正常还是面向对象方便好编程。
无状态
何为无状态?简单点讲就是,函数内部涉及的变量都是局部变量,不会像面向对象编程那样,共享类成员变量,也不会像面向过程编程那样,共享全局变量。函数的执行结果只与入参有关,跟其他任何外部变量无关。同样的入参,不管怎么执行,得到的结果都是一样的。
public int count(int a){
int b = 1;
return a+b;
}
public int count(int a,int b){
return a+b;
}
上面这俩第二个更符合无状态,因为第一个实际上b被改了之后,既保证不了,但是第二个什么情况,结果都是根据入参确定的,不会受到其他因素。
不同的编程范式:
面向对象的编程单元是类或对象,面向过程的编程单元是函数,函数式编程的编程单元是无状态函数。
Java的函数表达式三个体现:stream。Lambda,函数式接口
stream应该是用的最多的一个JDK8新特性,里面对应的很多方法,比如filter,map,groupBy等,但是要注意,实际上这些stream返回的都是一个Stream对象,而不是我们自己的对象,拿到的结果实际是从这个Stream对象转换出来的。
如下所示。其中,map、filter 是中间操作,返回 Stream 类对象,可以继续级联其他操作;max 是终止操作,返回的不是 Stream 类对象,无法再继续往下级联处理了。
public class FPDemo {
public static void main(String[] args) {
Optional result = Stream.of("f", "ba", "hello") // of返回Stream对象
.map(s -> s.length()) // map返回Stream对象
.filter(l -> l <= 3) // filter返回Stream对象
.max((o1, o2) -> o1-o2); // max终止操作:返回Optional
System.out.println(result.get()); // 输出2
}
}
lambda现在很多地方也会用到,如果不理解确实会导致代码阅读性门槛高,看到一篇文章,对Lambda总结,就是一个-> 箭头 的使用用法,其实这个不一定确切,但是你不懂的情况下,这个说法可以让你在一堆介绍lambda的繁琐的文章中有个理解。
lambda的结构
(a, b) -> { 语句1; 语句2;...; return 输出; } //a,b是输入参数
Lambda 表达式的写法非常灵活。我们刚刚给出的是标准写法,还有很多简化写法。比如,如果输入参数只有一个,可以省略 (),直接写成 a->{…};如果没有入参,可以直接将输入和箭头都省略掉,只保留函数体;如果函数体只有一个语句,那可以将{}省略掉;如果函数没有返回值,return 语句就可以不用写了。
实际上Lambda是Java提供的一种语法糖,底层实现还是函数接口,只不过这些都没有展示出来,利用的是默认实现。
下面代码是把上面代码的filte,map,max这些底层函数接口展示出来,这个你就能看到很多具体的步骤
Optional result = Stream.of("f", "ba", "hello")
.map(s -> s.length())
.filter(l -> l <= 3)
.max((o1, o2) -> o1-o2);
// 还原为函数接口的实现方式
Optional result2 = Stream.of("fo", "bar", "hello")
.map(new Function() {
@Override
public Integer apply(String s) {
return s.length();
}
})
.filter(new Predicate() {
@Override
public boolean test(Integer l) {
return l <= 3;
}
})
.max(new Comparator() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
Java 8 引入了 「 函数接口 」 ( funtional interface ) 的概念,「 函数接口 」就是那些有且只有显式定义一个方法的接口。
如果一个接口的实现类只需要实现一个方法,那么该接口就是函数接口。
具体来说,有以下两种情况
实际上,函数接口就是接口。不过,它也有自己特别的地方,那就是要求只包含一个未实现的方法。因为只有这样,Lambda
表达式才能明确知道匹配的是哪个接口。如果有两个未实现的方法,并且接口入参、返回值都一样,那 Java 在翻译 Lambda
表达式的时候,就不知道表达式对应哪个方法了。
Function、Predicate、Comparator 这些函数接口,他们包裹一个函数,把函数作为变量,弥补Java不能像,C语言那样把函数通过指针的方式直接使用。
接口 | 说明 |
---|---|
BiConsumer |
表示接受两个不同类型的参数,但不返回任何结果的操作 |
BiFunction |
表示接受两个不同类型的参数,并返回一个其它类型的结果的操作 |
BinaryOperator | 表示接受两个相同类型的参数,并返回一个同一类型的结果的操作 |
BiPredicate |
表示接受两个不同诶行的参数,且返回布尔类型的结果的操作 |
BooleanSupplier | 不接受任何参数,且返回一个布尔类型的结果的操作 |
Consumer | 表示接受一个参数,但不返回任何结果的操作 |
DoubleBinaryOperator | 表示接受两个 double 类型的参数,并返回 double 类型结果的操作 |
DoubleConsumer | 表示接受一个 double 类型的参数,但不返回任何结果的操作 |
DoubleFunction | 表示接受一个 double 类型的参数,且返回一个 R 类型的结果的操作 |
DoublePredicate | 表示一个接受两个 double 类型的参数, 且返回一个布尔类型的结果的操作 |
DoubleSupplier | 表示一个不接受任何参数,但返回布尔类型的结果的操作 |
DoubleToIntFunction | 表示接受两个 double 类型的参数,但返回一个 int 类型的结果的操作 |
DoubleToLongFunction | 表示接受两个 double 类型的参数,但返回一个 long 类型的结果的操作 |
DoubleUnaryOperator | 表示接受一个 double 类型的参数,且返回一个 double 类型的结果的操作 |
Function |
表示一个接受 T 类型的参数,且返回一个 R 类型结果的函数 |
IntBinaryOperator | 表示一个接受两个 int 类型的参数,且返回一个 int 类型的结果的操作 |
IntConsumer | 表示接受一个 int 类型的参数,但不返回任何结果的操作 |
IntFunction | 表示接受一个 int 类型的参数,但返回一个 R 类型的结果的操作 |
IntPredicate | 表示接受一个 int 类型的参数,但返回布尔类型的结果的操作 |
IntSupplier | 表示不接受任何参数,但返回一个 int 类型的结果的操作 |
IntToDoubleFunction | 表示接受一个 int 类型的参数,但返回一个 double 类型的结果的操作 |
IntToLongFunction | 表示接受一个 int 类型的参数,但返回一个 long 类型的结果的操作 |
IntUnaryOperator | 表示接受一个 int 类型的参数,且返回一个 int 类型的结果的操作 |
LongBinaryOperator | 表示接受两个 long 类型的参数,且返回一个 long 类型的结果的操作 |
LongConsumer | 表示不接受任何参数,但返回一个 long 类型的结果的操作 |
LongFunction | 表示接受一个 loing 类型的参数,但返回一个 R 类型的结果的操作 |
LongPredicate | 表示接受一个 long 类型的参数,但返回布尔类型的结果的操作 |
LongSupplier | 表示不接受任何参数,但返回一个 long 类型的结果的操作 |
LongToDoubleFunction | 表示接受一个 long 类型的参数,但返回一个 double 类型的结果的函数 |
LongToIntFunctio | 表示接受一个 long 类型的参数,但返回 int 类型的结果的函数 |
LongToDoubleFunction | 表示接受一个 long 类型的参数,但返回一个 double 类型的结果的函数 |
LongToIntFunction | 表示接受一个 long 类型的参数,但返回 int 类型的结果的函数 |
LongUnaryOperator | 表示接受一个 long 类型的参数,并返回一个 long 类型的结果的操作 |
ObjDoubleConsumer | 表示接受两个参数,一个为 T 类型的对象,另一个 double 类型,但不返回任何结果的操作 |
ObjIntConsumer | 表示接受两个参数,一个为 T 类型的对象,另一个 int 类型,但不返回任何结果的操作 |
ObjLongConsumer | 表示接受两个参数,一个为 T 类型的对象,另一个 double 类型,但不返回任何结果的操作 |
Predicate | 表示接受一个指定类型 T 的参数,但返回布尔类型的结果的操作 |
Supplier | 表示不接受任何参数,但返回一个 T 类型的结果的操作 |
ToDoubleBiFunction |
表示接受两个不同类型的参数,但返回一个 double 类型的结果的操作 |
ToDoubleFunction | 表示一个接受指定类型 T 的参数,并返回一个 double 类型的结果的操作 |
ToIntBiFunction |
表示接受两个不同类型的参数,但返回一个 int 类型的结果的操作 |
ToIntFunction | 表示一个接受指定类型 T 的参数,并返回一个 int 类型的结果的操作 |
ToLongBiFunction |
表示接受两个不同类型的参数,但返回一个 long 类型的结果的操作 |
ToLongFunction | 表示接受两个不同类型的参数,但返回一个 long 类型的结果的操作 |
UnaryOperator | 表示接受一个参数,并返回一个与参数类型相同的结果的操作 |