深入浅出聊聊Java函数式编程思想
作者:互联网
引言
一直以来,Java都被认为是一种面向对象的编程语言,“万事万物皆对象”的思想已经深入人心。但随着Java8的发布,一切看起来似乎有些改变。Lambda表达式和Stream的引入,让Java焕发了新的活力,它允许人们可以用函数式编程思维思考问题。本文主要介绍了函数式编程思想在Java中的应用。
指令式还是声明式?
先看一段代码:计算商品价格的最大值。 我们一般会这样实现:
int max = 0; for(int price : prices) { if (max < price) { max = price; } }
这就是典型的“指令式”(imperative)写法。它利用计算机的指令或者语法,告诉计算机一步步要做什么。 针对同样的功能,再看看另一种写法:
int max = prices.stream().reduce(0, Math::max);
如果熟悉软件设计原则的读者会发现,这正是“好莱坞原则”的使用,Tell,Don’t Ask!
用函数式思想实现设计模式
在GoF的经典著作《设计模式》一书中,详细介绍了23种常见的设计模式。细心的读者可能会发现,书名下面还有一行小字:可复用面向对象软件的基础。也就是说它是用面向对象思想实现的。这么多年过去了,对设计模式的争论一直在进行,但这已不再重要,今天让我们以函数式编程的角度重新审视设计模式。
命令模式
命令模式一般会对命令进行封装,对外提供接口供使用者调用。 先看一个例子:扫地机器人可以执行直行、左转、右转等指令操作。 先定义指令接口和实现类:
// 命令接口 public interface Command { void execute(); } // 前进命令实现 public class forward implements Command { public void execute() { System.out.println("go forward"); } } // 右转命令实现 public class Right implements Command { @Override public void execute() { System.out.println("go right"); } }
下面实现机器人:
public class Robot { public static void move(Command... commands) { for (Command command : commands) { command.execute(); } } public static void main(String[] args) { Robot.move(new Forward(), new Right()); } }
虽然功能实现了,但有一个问题就是:创建的类太多!
我们看看函数式编程怎么实现?
因为Command接口中的execute()是一个无入参和无返回结果的方法,这让我们很自然的想起了Java内置的函数式接口Runnable,它也有一个同样签名的run()方法。虽然Runnable接口本来是用在多线程处理中的,但这里我们取巧的用在函数式编程中。
首先我们把Robot类的move()方法的入参替换为Runnable接口:
public static void flexibleMove(Runnable... commands) { Stream.of(commands).forEach(Runnable::run); } 复制代码
这样一来,我们只需要在类方法中实现命令就可以了。
public static void forward() { System.out.println("go forward"); } public static void right() { System.out.println("go right"); }
调用就变成了:
Robot.flexibleMove(Robot::forward, Robot::right);
这种实现减少了很多命令实现类,把焦点更多放在业务逻辑上。