一文读懂Java函数式编程

函数式编程中没有赋值语句,因此变量一旦有了值就不会再改变。更通俗的讲,函数式编程不修改变量,这样消除了bug的一个主要来源,也使得执行顺序变得无关紧要。这意味着状态不能保存在变量中——即程序是“引用透明”的。函数式编程使用参数保存状态,避免了使用程序状态和可变对象,降低了程序复杂度,而这也正是函数式编程的精髓。函数式编程强调执行的结果,而非执行的过程。我们先构建一系列简单却具有一定功能的小函数,然后再将这些函数进行组装以实现完整的逻辑和复杂的运算,这是函数式编程的基本思想。

1. 什么是函数式编程Functional Programming

我们查看wikipedia上的相关定义,我们可以总结出以下几点

1.1 函数是"第一等公民"

指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。

1.2 闭包和高阶函数

闭包是起函数的作用并可以像对象一样操作的对象。与此类似,函数式编程语言支持高阶函数。高阶函数可以用另一个函数(间接地,用一个表达式) 作为其输入参数,在大多数情况下,它甚至返回一个函数作为其输出参数。这两种结构结合在一起使得可以用优雅的方式进行模块化编程,这是使用函数式编程的最大好处

1.3 不改变状态(引用透明)

函数式编程只是返回新的值,不修改系统变量。因此,不修改变量,也是它的一个重要特点。在其他类型的语言中,变量往往用来保存"状态"(state)

什么是引用透明?
如果一个函数只会受到入参的变化,那么这个函数每次的调用都会是相同的
一个函数f(x),里面调用了g(x),g(x)里面又调用了h(x),h(x)最终计算出了结果,作为f(x)的返回值返回了。如果所有的状态都没有改变,f(x)下一次再调用相同的参数的时候,应该会得到完全一样的结果,那这个时候其实不用再调用g(x)和h(x)了,也可以得到完全一样的结果。当一个函数,不依赖“外部”变量和状态,只依赖入参的变化而影响函数最终返回值,也就是说入参相同,得到的返回值结果一定相同,如果函数具有这种性质,就可以说这个函数是引用透明的。

1.4 递归(使用参数保存状态,而非变量)

函数式编程是用递归做为控制流程的机制。

1.5 只用"表达式",不用"语句",没有副作用

“表达式”(expression)是一个单纯的运算过程,总是有返回值;“语句”(statement)是执行某种操作,没有返回值。函数式编程要求,只使用表达式,不使用语句。也就是说,每一步都是单纯的运算,而且都有返回值。
原因是函数式编程的开发动机,一开始就是为了处理运算(computation),不考虑系统的读写(I/O)。“语句"属于读写操作,所以就被排斥在外。函数式编程强调没有"副作用”,意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。

1.5.1 什么是副作用

在stackoverflow中有一个提问Side Effects in Functional Programming
在函数式编程中,如下的行为是称之为副作用的:

  1. 修改一个变量
  2. 修改一个对象的字段值
  3. 抛出异常
  4. 在控制台显示信息、从控制台接收输入
  5. 在屏幕上显示(GUI)
  6. 读写文件、网络、数据库。

如果函数式编程中,我们没有任何输入、输出等有副作用的操作,能编写什么样的程序呢?如果能,又是怎么做到的?
函数式编程是一种隔离应用逻辑(表达)与实际运行时解释的方法。你的“函数式代码”用来表达(但不执行)所需要达成的执行效果,返回某种形式的数据结构来描述这个计算结果。然后,我们会在一个解释器中来执行该结果,后者不是函数式的。
完全的纯函数式编程是不可能的,一段纯函数式代码,除了会让CPU产生热量,不会产生其他副作用,我们还需要一个解释器来真实的进行IO操作,如读写文件、网络等。将两者隔离会带来很多的优势:纯函数式代码,更易于测试,引用透明性会让代码可读性提升,提高开发阶段的效率,减少BUG,提高产品质量。同时,函数式编程,相比命令式编程,可以让很多复杂的代码便得更简单。比如:Tired of Null Pointer Exceptions? Consider Using Java SE 8’s Optional!
因此,**在函数式语言中,处理副作用的方式就是隔离副作用,使用纯函数式的代码,来计算”副作用“,并将其表示为一个”值“,然后将该值交给一个解释器来执行 **

2. Java中的函数接口

2.1. Function

Function是从T到R的一元映射函数。将参数T传递给一个函数,返回R。即R = Function(T)

public static <T,U> U  change(T t, Function<T,U> f, Class<U> uClass) {
        U u =  f.apply(t);
        return u;
}
Integer change = change("6789", Integer::parseInt, Integer.class);
System.out.println(change); // 6789
List change = change("11", (t) -> {
            List list = new LinkedList();
            int i = Integer.parseInt(t);
            list.add(i*i);
            return list;
}, List.class);
System.out.println(change.toString()); // [121]

2.2. Predicate

Predicate是一个谓词函数,主要作为一个谓词演算推导真假值存在,返回布尔值的函数。Predicate等价于一个Function的boolean型返回值的子集。
predicate最常用的莫过于 Stream filter(Predicate<? super T> predicate);

public static <T> boolean checkString(T s, Predicate<T> p){
        return p.test(s);
}
boolean b = checkString("testse", (t) -> t.length() > 5);
System.out.println(b); // true
boolean c = checkString(987654321, (t) -> t < 5);
System.out.println(c); // false

2.3. Consumer

Consumer是从T到void的一元函数,接受一个入参但不返回任何结果的操作。
Consumer最常用的肯定是 default void forEach(Consumer<? super T> action) {}

public static <T> void consumerTest(T input, Consumer<T> consumer) {
        consumer.accept(input);
}
consumerTest("Consumer test string", n -> System.out.println(n + " add suffix"));
// Consumer test string add suffix

2.4. Supplier

java.util.function.Supplier 接口仅包含一个无参的方法:T get(),用来获取一个泛型参数指定类型的对象数据
该接口被称为生产型接口,指定接口的泛型是什么类型,那么get方法就会产生什么类型的数据

public static <T> T supplierTest(Supplier<T> supplier) {
        return supplier.get();
}
System.out.println(supplierTest(() -> 1024 >> 2)); // 256

2.5. BiFunction

java.util.function.BiFunction<T, U, R>接收两个参数返回一个结果,如果接收参数和返回参数的类型相同,就可用java.util.function.BinaryOperator替换。

private static  <T,P> void biFunctionTest(T container, P param, BiFunction<T,P,T> function) {
        function.apply(container, param);
}
Map<String, String> map = new HashMap<>();
biFunctionTest(map, "test_param", (t, p) -> {
    t.put("url",  "http://" + p);
    return t;
});
biFunctionTest(map, 6666666, (t, p) -> {
    t.put("number", String.valueOf(p));
    return t;
});
System.out.println(map);
// {number=6666666, url=http://test_param}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值