【学习笔记】Java函数式编程01-Lambda表达式
一、概述
1.1 为什么学习函数式编程
- 看懂代码
- 提高处理大数据量集合的效率
- 代码可读性提升
- 消灭嵌套地狱(反复的嵌套for …if …for …if …导致可读性下降
1.2 函数式编程思想
1.2.1 概念
类似数学中的函数,主要关注对数据进行的操作
- 面向对象思想:关注什么对象做了什么事情。
- 函数式编程:并不关心对象和具体的参数类型,而是关注对数据进行了什么操作
1.2.2 优点
- 代码简洁(开发快速
- 接近自然语言,易于理解(需要学习成本
- 易于并发编程(并行流/对大数据的处理效率很高
二、Lambda表达式
2.1 概述
Lambda表达式可以理解为JDK8新增了一个语法糖。
- 对某一些匿名内部类的写法进行简化。
- 是函数式编程的重要体现
- 让程序员不用关注对象,而关注对数据的操作
关于语法糖:语法糖(Syntactic sugar),也译为糖衣语法
[外链图片转存中…(img-ultqjqy9-1703122023748)]
指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。
通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。
2.2 核心原则
可推导、可省略
- 如果一个参数的类型可以被推导出来,则可以省略参数类型
- 如果方法名可以被推导出来,则可以省略方法名
2.3 基本格式
(参数列表) —> {代码}
2.3.0 小结
要理解函数式编程,一开始不要想着一次性写出最简单的写法,应该从匿名内部类的写法开始逐步优化,循序渐进。
2.3.1 案例一:匿名内部类写法
创建一个Runnable接口的实现类为构造参数的线程类。
new Thread() // 线程的构造函数可以传入一个Runnable接口的实现类
- 实现类可以用匿名内部类的形式书写,如下
new Thread(new Runnable() {
@Override
public void run(){
System.out.println("异步执行的方法")
}
}).start();
也可以使用Lambda表达式进行简化优化,延伸一个问题,
🌟什么情况可以进行Lambda进行简化?
- 这个匿名内部类是一个接口的实现,并且只有一个抽象方法需要重写
- 即:接口+单方法
函数式写法遵循的核心原则之一是**“可省略”**,那么方法的5要素有哪些部分是可以省略的?
- 作用域
- 返回值
- 方法名
- 参数列表
- 方法体
所以由此也可推证:函数式关注的重点是“数据+对数据的操作”
经过简化后的代码:
// (略去了方法名、作用域、返回值)
new Thread(() -> {
System.out.println("线程运行");
}).start();
2.3.2 案例二:IntBinaryOperator(计算
先定义一个方法如下,练习如何调用该方法
public static int calculate(IntBinaryOperator operator){
int a = 10;
int b = 20;
return operator.applyAsInt(a , b);
}
查看源码可以发现,IntBinaryOperator是一个单方法的接口,符合函数式编程的写法
public interface IntBinaryOperator {
int applyAsInt(int left, int right);
}
为了便于初学,我们选择一开始先写匿名内部类,然后再进行简化,如下
public static void main(String[] args) {
int calculate = calculate(new IntBinaryOperator() {
@Override
public int applyAsInt(int left, int right) {
return left + right;
}
});
System.out.println("没有简写的计算结果:"+calculate);
int calculate1 = calculate((int left, int right) -> {
return left + right;
});
System.out.println("简写后的计算结果:"+calculate1);
}
- 不难发现,这个接口的作用就是让程序员自定义
int left, int right
的计算逻辑
??知识点延伸:IDEA的快捷操作
使用alt+enter
快捷键可以在匿名内部类和lambda表达式之间快速切换
[外链图片转存中…(img-2AAOswwC-1703122023750)]
[外链图片转存中…(img-sT7c6k9G-1703122023750)]
2.3.3 案例三: IntPredicate(断言
定义下面的方法,练习如何调用
public static void predicate(IntPredicate intPredicate) {
int[] arr = {0, 2, 3, 4, 5, 6, 7, 11, 23, 55, 120, 560, 777, 8888, 9999};
for (int i : arr) {
if (intPredicate.test(i)) {
System.out.println(i + "\t断言为true");
}
}
}
查看IntPredicate源码,第一眼会发现这个接口有多个方法,但是仔细看就会发现,其实只有一个抽象方法boolean test(int value)
,所以符合lambda简化的要求——只有一个抽象方法的接口
public interface IntPredicate {
boolean test(int value);
// 。。。。 其他方法
}
练习代码如下:
public class Main03 {
public static void main(String[] args) {
// 匿名内部类写法
predicate(new IntPredicate() {
@Override
public boolean test(int value) {
return value%2 == 0;
}
});
// lambda简化写法
predicate((int value) -> {
return value%2 == 0;
});
}
public static void predicate(IntPredicate intPredicate) {
int[] arr = {0, 2, 3, 4, 5, 6, 7, 11, 23, 55, 120, 560, 777, 8888, 9999};
for (int i : arr) {
if (intPredicate.test(i)) {
System.out.println(i + "\t断言为true");
}
}
}
}
2.3.4 案例四:Function (方法泛型
定义下面这个方法,练习如何调用
public static <R> R typeConvert(<String, R> function) {
String str = "666";
return function.apply(str);
}
查看Function<T, R>
源码,可以看到方法利用泛型参数指定了apply方法的传入的参数类型和返回的参数类型
和案例三源码一样,其他的默认方法先不看,关注其中一个抽象方法即可
public interface Function<T, R> {
R apply(T t);
// 。。。。其他方法
}
在案例四里,已经将泛型T定义为String,即默认Funtion传入的参数为String,而返回的类型可以让方法的调用者来定义。
练习代码
public class Main04 {
public static <R> R typeConvert(Function<String, R> function) {
String str = "666";
return function.apply(str);
}
public static void main(String[] args) {
// 匿名类写法
Integer integer = typeConvert(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.valueOf(s);
}
});
System.out.println("匿名类写法结果:" + integer);
// Lambda简写优化写法
Integer integer1 = typeConvert((String s) -> {
return Integer.valueOf(s);
});
System.out.println("lambda写法结果:" + integer1);
}
}
- 这个方法将typeConvert作用为将string转换为integer的一个方法
?? 在学习初期,先不要求手写接口,先学会如何[调用和使用]即可
可以从java提供的一些方法入手,循序渐进的进行联系
2.3.5 案例五 IntConsumer(消费者
IntConsumer也是java自带的函数接口,是一个没有返回值的消费方法,不赘述,直接上代码。
方法代码
public static void accept(IntConsumer consumer){
int[] arr = {0, 2, 3, 4, 5, 6, 7, 11, 23, 55, 120, 560, 777, 8888, 9999};
for (int i : arr) {
consumer.accept(i);
}
}
使用代码
public static void main(String[] args) {
accept(new IntConsumer() {
@Override
public void accept(int value) {
System.out.println("消费int的数值:"+value);
}
});
accept((int value) -> {
System.out.println("消费int的数值:"+value);
});
}
2.4 省略规则
- 参数类型可以省略
- 方法体只有一句代码时,大括号return和唯一一句代码的分号可以省略
- 方法只有一个参数时,小括号可以省略
以上规则记不住,也可以不省略(废话
这里的规则也是遵循“可推导、可省略”的大原则,不建议死记硬背
2.4.1 参数类型可以省略
首先这个规则符合“参数类型可推导”的大原则,实际上,当代码能确认这个lambda表达式所指代的抽象方法时,参数类型也就随之确认了。
比如案例二的写法就可以再省略为:
int calculate1 = calculate((int left, int right) -> {
return left + right;
});
// ####简化参数类型####
int calculate2 = calculate((a, b) -> {
return a + b;
});
2.4.2 方法体只有一句代码时的省略规则
方法体只有一句代码时,可以省略:
- 大括号可以省略
- return可以省略
- 分号可以省略
注意:以上三个必须一起省略,否则会编译报错。.
还是以案例2为例子,进行进一步优化
// ####简化参数类型####
int calculate2 = calculate((a, b) -> {
return a + b;
});
// ####只有唯一一句代码时####
int calculate3 = calculate((a, b) -> a + b );
[外链图片转存中…(img-1YuQkeTB-1703122023751)]
2.4.3 方法只有一个参数时,小括号可以省略
很好理解,也是符合“可推导、可省略”的大原则,当只有一个参数时,参数类型肯定是确定的,即使略去小括号也不会产生歧义。
以案例三基础上进行简化
// lambda简化写法
predicate((int value) -> {
return value%2 == 0;
});
// ###方法只有一个参数时,小括号可以省略###
predicate(value -> value%2 ==0);
[外链图片转存中…(img-i47xrR8Y-1703122023753)]
2.4.4 以上规则记不住可以不记
使用IDEA的快捷键
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!