你真的了解 Java 8 中的 lambda 表达式、方法引用、函数式接口、默认方式、静态方法吗
lambda 表达式
lambda 表达式在项目中也是用到了,这种新的语法的加入,对于使用 Java 多年的我,我觉得是如虎添翼的感觉哈,这种新的语法,大大的改善了以前的 Java 的代码,变得更加的简洁,我觉得这也是为什么 Java8 能够很快的流行起来的原因吧。
这里我们用几个以前的经典的 Java 的写法和用 lambda 表达式的方式进行对比。
线程的用法
原始的线程用法
1//使用匿名内部类的方式启动多线程
2 new Thread(new Runnable() {
3 @Override
4 public void run() {
5 System.out.println("这是使用匿名内部类的方式。。。");
6 }
7 }).start();
lambda 表达式
1//使用lambda表达式方式
2 new Thread(() -> {
3 System.out.println("这是使用lambda表达式的方式。。。");
4 }).start();
你会发现,用 lambda 表达式的方式能够写更少的代码,看起来也会更加的舒服和简洁。
这里没有使用参数,只是一个简单的例子。
我们再看一个例子。
遍历方式
原始方式
1//原始方式
2 List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
3 for (int i : list) {
4 System.out.println(i);
5 }
lambda 表达式方式
1//使用lambda表达式代替foreach循环
2 Stream.of(1, 2, 3, 4, 5).forEach((x) -> {
3 System.out.println(x);
4 });
在原始的方式中,我们一般使用 foreach 的方式进行遍历,有了 Java8 的方式之后,我们可以用 forEach
方法,然后,再用 lambda 表达式的方式进行遍历,也让原来的方式变得更加的简洁。
在这个例子中,我们加了一个参数,在()
中间我们加了一个 x
,代表的意思其实是:通过 forEach
方法,我们把一个元素已经赋值到 x
中了,拿到这个 x
,我们就可以输出结果。
总结
lambda 的使用方式其实很简单,可以总结为下面的方法。
1([参数可选,...]) -> {
2}
方法引用
方法引用其实是 lambda 表达式的部分的简化,也就是为了简化 lambda 表达式而存在的感觉,下面我们还讲讲怎么使用方法引用。
1/**
2 * @return void
3 * @Author ouyangsihai
4 * @Description 方法引用测试
5 * @Date 10:23 2019/5/14
6 * @Param []
7 **/
8 @Test
9 public void test_method_reference() {
10 //使用lambda表达式
11 Stream.of("A", "BB", "CCC", "DDDD", "FFFFF")
12 .map(s -> s.length()) //lambda
13 .forEach((x) -> {
14 System.out.println(x);
15 });
16
17 //使用静态方法引用
18 Stream.of("A", "BB", "CCC", "DDDD", "FFFFF")
19 .map(String::length) //静态方法引用
20 .forEach((x) -> {
21 System.out.println(x);
22 });
23
24 //使用实例方法引用
25 Stream.of(
26 new ClassMate("1", "欧阳思海"),
27 new ClassMate("2", "sihai")
28 ).map(ClassMate::getName)//实例方法引用
29 .forEach(x -> {
30 System.out.println(x);
31 });
32
33 }
在第一个测试中,我们用的是 lambda 表达式来获取每个字符串的长度。
1s -> s.length()
在第二个测试中,我们使用的是静态方法引用来获取每个字符串的长度。
1String::length
在第三个测试中,我们使用的是实例方法引用。
1ClassMate::getName
解释 ① map 方法是映射的意思。 ② forEach 方式是遍历每一个元素。 ③ ClassMate 是一个包含 id 和 name 的简单 po 类。
通过上面这个例子,基本上我们就知道怎么使用方法引用了。下面我们进行一个小的总结。
总结 ① 使用方法
1类名::方法名
② 方法可以是:静态方法,实例方法
构造函数引用
在上面我们讲了方法引用的基本使用方法,其实除了方法引用以外,还有构造函数引用,回想一下,以前我们创建对象是怎么做?是不是需要 new 一个对象呢,那么现在用构造函数引用又是怎么做的呢?
下面我们用一个例子讲解一下,在这个例子中,对象还是使用上面的 ClassMate
。
1/**
2 * @return void
3 * @Author ouyangsihai
4 * @Description 构造函数引用测试
5 * @Date 10:23 2019/5/14
6 * @Param []
7 **/
8 @Test
9 public void test_method_reference2() {
10 //使用lambda表达式
11 Stream.of("A", "BB", "CCC", "DDDD", "FFFFF")
12 .map(s -> new ClassMate(s)) //lambda
13 .collect(Collectors.toList());
14
15 //使用构造函数引用
16 Stream.of("A", "BB", "CCC", "DDDD", "FFFFF")
17 .map(ClassMate::new) //构造函数引用,由上下文决定用哪一个构造函数
18 .collect(Collectors.toList());
19 }
① 第一个我们使用的是 lambda 表达式进行创建对象的 s -> new ClassMate(s)
。
② 第二个我们使用的是构造函数引用创建对象的 ClassMate::new
。
③ 我们发现构造函数引用:类名::new
,然后对于使用哪一个构造函数是由上下文决定的,比如有一个参数和两个参数和无参数的构造函数,会自动确定用哪一个。
接口
在 Java 8 之前的接口是不能有实现的,只能定义抽象方法,然而,在 Java 8 以后,增加了一个新的功能,可以添加实现,可以定义默认方法,可以定义静态方法。
函数式接口
什么是函数式接口呢?
这个名词在 Java 中以前是很少听到的,但是正是有了 Java 8 的横空出世,函数式编程也变得熟悉了。
在一个接口中我们以 @FunctionalInterface
注解声明一个接口,并且接口中只有一个抽象方法,那么我们就叫做这是一个函数式接口。
1/**
2 * @ClassName FunctionalInterfaceTest
3 * @Description
4 * @Author 欧阳思海
5 * @Date 2019/5/14 10:39
6 * @Version 1.0
7 **/
8@FunctionalInterface
9public interface FunctionalInterfaceTest {
10 //继承接口后,又加了新的抽象方法,这个接口就不再是函数式接口
11 void test(String s);
12}
① 上面的接口中只有一个抽象方法,所以这是一个函数式接口。 ② 如果上面接口中再加一个抽象方法,那么就不是函数式接口了。
下面,我们再通过继承来继承这个接口。
1/**
2 * @ClassName FunctionalTest
3 * @Description
4 * @Author 欧阳思海
5 * @Date 2019/5/17 17:26
6 * @Version 1.0
7 **/
8public interface FunctionalTest extends FunctionalInterfaceTest{
9
10 int test2();
11}
① 我们继承了上面的接口,并且加了一个 test2
方法。
② 这里注意,如果一个接口集成现有的函数式接口后,又加了其他的抽象方法,这个接口就不是函数式接口了。
默认方法
默认方法很简单,用 default
声明即可。
1/**
2 * @ClassName FunctionalInterfaceTest
3 * @Description
4 * @Author 欧阳思海
5 * @Date 2019/5/14 10:39
6 * @Version 1.0
7 **/
8@FunctionalInterface
9public interface FunctionalInterfaceTest {
10 //继承接口后,又加了新的抽象方法,这个接口就不再是函数式接口
11 void test(String s);
12
13 //默认方法
14 default String getStr(){
15 return null;
16 }
17}
① 在接口中添加了一个默认方法。并且实现了方法。
静态方法
默认方法很简单,用 static
声明即可。
1/**
2 * @ClassName FunctionalInterfaceTest
3 * @Description
4 * @Author 欧阳思海
5 * @Date 2019/5/14 10:39
6 * @Version 1.0
7 **/
8@FunctionalInterface
9public interface FunctionalInterfaceTest {
10 //继承接口后,又加了新的抽象方法,这个接口就不再是函数式接口
11 void test(String s);
12
13 //静态方法
14 static String getStr2(){
15 return null;
16 }
17
18 //错误用法
19 default static String getStr3(){
20 return null;
21 }
22}
① 实现的静态方法,用 static
声明。
② 注意不能同时使用 default 和 static 声明。
- 【译】ASP.NET MVC 6路由技术
- 从原理到实践:Oracle 12.2 Sharding技术揭秘
- Xamarin-C#开发移动App-环境搭建
- Tensorflow快速入门
- .NET Core 实战笔记1-介绍和安装
- 全面直观认识深度神经网络
- dedecms清空所有文章怎么操作?sql语句如何写?
- .NET Core 实战笔记2-从命令开始
- 【译】使用Docker Compose一条指令配置Mesos
- 【译】Windows下的Docker Machine - 如何设置你的Docker主机
- 史上最透彻的KMP算法讲解
- 【译】助你成功搭建云应用的12条方法
- 你能用微信小程序打开小程序了【附开发方法】
- Logistic回归实战篇之预测病马死亡率(一)
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Spring Boot 开箱即用
- Qt音视频开发2-vlc回调处理
- cmake学习
- 数据分析:在缓慢变化中寻找跳变——基于缓慢变化维度的用户分群
- 02 Confluent_Kafka权威指南 第二章:安装kafka
- Kafka集群搭建过程(kafka2.5+eagle)
- 07 Confluent_Kafka权威指南 第七章: 构建数据管道
- java中的reference(二): jdk1.8中Reference的源码阅读
- 08 Confluent_Kafka权威指南 第八章:跨集群数据镜像
- java的reference(五): WeakReference的应用之二--InheritableThreadLocal源码分析
- rabbitmq-3.7.7安装过程
- 09 Confluent_Kafka权威指南 第九章:管理kafka集群
- 关于禁止使用Executors创建线程池的分析
- ArrayList源码分析(基于jdk1.8)(一):源码及基本操作
- ArrayList源码分析(基于jdk1.8)(二):subList陷阱