网站首页 > java教程 正文
在众多的编程范式中,大多数开发人员比较熟悉的是面向对象编程范式。一方面是由于面向对象编程语言比较流行,与之相关的资源比较丰富,比如Java,c++等。
另外一方面是由于大部分学校和培训机构的课程设置,都选择流行的面向对象编程语言。面向对象编程范式的优点在于其抽象方式与现实中的概念比较相近。比如,学生、课程、汽车和订单等这些现实中的概念,在抽象成相应的类之后,我们很容易就能理解类之间的关联关系。这些类中所包含的属性和方法可以很直观地设计出来。
函数式编程范式则相对较难理解。这主要是由于函数所代表的是抽象的计算,而不是具体的实体。因此比较难通过类比的方式来理解。举例来说:
在一个学生信息管理系统中,可能会需要找到一个班级的某门课程的最高分数;在一个电子商务系统中,也可能会需要计算一个订单的总金额。看似风马牛不相及的两件事情,其实都包含了同样的计算在里面。
那么他们的共同点是什么呢?
对一个可迭代的对象进行遍历,同时在遍历的过程中执行自定义的操作。在计算最高分数的场景中,在遍历的同时需要保存当前已知最高分数,并在遍历过程中更新该值;在计算订单总金额的场景中,在遍历的同时需要保存当前已累积的金额,并在遍历过程中更新该值。
上面的话还是太抽象了,用直观的代码来表示:计算学生的最高分数的代码
int maxMark = 0;
for (Student student : students) {
if (student.getMark() > maxMark) {
maxMark = student.getMark();
}
}
计算订单的总金额的代码
BigDecimal total = BigDecimal.ZERO;
for (LineItem item : order.getLineItems()) {
total = total.add(item.getPrice().multiply(new BigDecimal(item.getCount())));
}
你可能还会问题,这两段代码不一样啊,好了,用我们面向对象的抽象来提取下上面两段代码的共性:该计算模式由 3 个部分组成:
保存计算结果的状态,有初始值。遍历操作。遍历时进行的计算,更新保存计算结果的状态值。
下面,我们把这 3 个元素提取出来,用代码表示
reduce(students, (mark, student) -> {
return Math.max(student.getMark(), mark);
}, 0);
reduce(order.lineItems, (total, item) -> {
return total.add(item.getPrice().multiply(new BigDecimal(item.getCount())))
}, BigDecimal.ZERO);
了解函数式编程的读者应该已经看出来了,这就是常用的 reduce 函数。
Java中的函数式编程
作为面向对象的编程语言,Java 中使用接口来表示函数。直到 Java 8,Java 才提供了内置标准 API 来表示函数,增加了java.util.function 包
Function<T, R>
Function<T, R>定义在java.util.function 包。
Function<T, R> 表示接受一个参数的函数,输入类型为 T,输出类型为 R。
Function 接口只包含一个抽象方法 R apply(T t),也就是在类型为 T 的输入 t 上应用该函数,得到类型为 R 的输出。
除了接受一个参数的 Function 之外,还有接受两个参数的接口 BiFunction<T, U, R>,T 和 U 分别是两个参数的类型,R 是输出类型。BiFunction 接口的抽象方法为 R apply(T t, U u)。超过 2 个参数的函数在 Java 标准库中并没有定义。
除了 Function 和 BiFunction 之外,Java 标准库还提供了几种特殊类型的函数:
- Consumer<T>:接受一个输入,没有输出。抽象方法为 void accept(T t)。
- Supplier<T>:没有输入,一个输出。抽象方法为 T get()。
- Predicate<T>:接受一个输入,输出为 boolean 类型。抽象方法为 boolean test(T t)。
- UnaryOperator<T>:接受一个输入,输出的类型与输入相同,相当于 Function<T, T>。
- BinaryOperator<T>:接受两个类型相同的输入,输出的类型与输入相同,相当于 BiFunction<T,T,T>。
- BiPredicate<T, U>:接受两个输入,输出为 boolean 类型。抽象方法为 boolean test(T t, U u)。
@FunctionalInterface
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。有意思的是,这个抽象方法的方法名无所谓,因为函数式接口可以被隐式转换为 lambda 表达式,lambda 是不需要方法名的。
Lambda 表达式和方法引用(实际上也可认为是Lambda表达式)上。如定义了一个函数式接口如下:
@FunctionalInterface
interface GreetingService
{
void sayMessage(String message);
}
那么就可以使用Lambda表达式来表示该接口的一个实现(注:JAVA 8 之前一般是用匿名类实现的):
GreetingService greetService1 = message -> System.out.println("Hello " + message);函数式接口可以对现有的函数友好地支持 lambda。
内容太多,篇幅原因待续
猜你喜欢
- 2024-10-07 python函数式编程(python函数经典例程)
- 2024-10-07 JavaScript 的函数式编程与面向对象编程区别在哪?
- 2024-10-07 函数式编程:Python和Java的实现方式有哪些?
- 2024-10-07 函数式编程与命令式编程的区别以及函数式编程不需要锁
- 2024-10-07 前端同学如何函数式编程?(前端函数式编程和响应式编程)
- 2024-10-07 java8精华-函数式编程-Function (五)
- 2024-10-07 Java函数式编程实战:Consumer、Predicate和Supplier的使用指南
- 2024-10-07 java码农福音:函数式编程和lambda表达式
- 2024-10-07 深入理解Java中的Lambda表达式和函数式编程的关系
- 2024-10-07 Java 的函数式接口(必懂知识点!)(jdk1.8函数式接口)
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)