# 《Java8新特性》第03章:Lambda表达式基础语法

# 写在前面

前面积极响应读者的需求,写了两篇Java新特性的文章。有小伙伴留言说:感觉Lambda表达式很强大啊!一行代码就能够搞定那么多功能!我想学习下Lambda表达式的语法,可以吗?我的回答是:没问题!这不,Lambda表达式来了!

# 匿名类到Lambda表达式

我们先来看看从匿名类如何转换到Lambda表达式呢?

这里,我们可以使用两个示例来说明如何从匿名内部类转换为Lambda表达式。

  • 匿名内部类到Lambda表达式

使用匿名内部类如下所示。

Runnable r = new Runnable(){
    @Override
    public void run(){
        System.out.println("Hello Lambda");
    }
}
1
2
3
4
5
6

转化为Lambda表达式如下所示。

Runnable r = () -> System.out.println("Hello Lambda");
1
  • 匿名内部类作为参数传递到Lambda表达式作为参数传递

使用匿名内部类作为参数如下所示。

TreeSet<Integer> ts = new TreeSet<>(new Comparator<Integer>(){
    @Override
    public int compare(Integer o1, Integer o2){
        return Integer.compare(o1, o2);
    }
});
1
2
3
4
5
6

使用Lambda表达式作为参数如下所示。

TreeSet<Integer> ts = new TreeSet<>(
	(o1, o2) -> Integer.compare(o1, o2);
);
1
2
3

从直观上看,Lambda表达式要比常规的语法简洁的多。

# Lambda表达式的语法

Lambda表达式在Java语言中引入了 “->” 操作符, “->” 操作符被称为Lambda表达式的操作符或者箭头操作符,它将Lambda表达式分为两部分:

  • 左侧部分指定了Lambda表达式需要的所有参数。

Lambda表达式本质上是对接口的实现,Lambda表达式的参数列表本质上对应着接口中方法的参数列表。

  • 右侧部分指定了Lambda体,即Lambda表达式要执行的功能。

Lambda体本质上就是接口方法具体实现的功能。

我们可以将Lambda表达式的语法总结如下。

1.语法格式一:无参,无返回值,Lambda体只有一条语句

Runnable r = () -> System.out.println("Hello Lambda");
1

具体示例如下所示。

@Test
public void test1(){
    Runnable r = () -> System.out.println("Hello Lambda");
    new Thread(r).start();
}
1
2
3
4
5

2.语法格式二:Lambda表达式需要一个参数,并且无返回值

Consumer<String> func = (s) -> System.out.println(s);
1

具体示例如下所示。

@Test
public void test2(){
    Consumer<String> consumer = (x) -> System.out.println(x);
    consumer.accept("Hello Lambda");
}
1
2
3
4
5

3.语法格式三:Lambda只需要一个参数时,参数的小括号可以省略

Consumer<String> func = s -> System.out.println(s);
1

具体示例如下所示。

@Test
public void test3(){
    Consumer<String> consumer = x -> System.out.println(x);
    consumer.accept("Hello Lambda");
}
1
2
3
4
5

4.语法格式四:Lambda需要两个参数,并且有返回值

BinaryOperator<Integer> bo = (a, b) -> {
    System.out.println("函数式接口");
    return a + b;
};
1
2
3
4

具体示例如下所示。

@Test
public void test4(){
    Comparator<Integer> comparator = (x, y) -> {
        System.out.println("函数式接口");
        return Integer.compare(x, y);
    };
}
1
2
3
4
5
6
7

5.语法格式五:当Lambda体只有一条语句时,return和大括号可以省略

BinaryOperator<Integer> bo = (a, b) -> a + b;
1

具体示例如下所示。

@Test
public void test5(){
    Comparator<Integer> comparator = (x, y) ->  Integer.compare(x, y);
}
1
2
3
4

6.语法格式六:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器能够通过上下文推断出数据类型,这就是“类型推断”

BinaryOperator<Integer> bo = (Integer a, Integer b) -> {
    return a + b;
};
1
2
3

等同于

BinaryOperator<Integer> bo = (a, b) -> {
    return a + b;
};
1
2
3

上述 Lambda 表达式中的参数类型都是由编译器推断得出的。 Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。 Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”。

# 函数式接口

Lambda表达式需要函数式接口的支持,所以,我们有必要来说说什么是函数式接口。

只包含一个抽象方法的接口,称为函数式接口。

可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。

可以在任意函数式接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。

我们可以自定义函数式接口,并使用Lambda表达式来实现相应的功能。

例如,使用函数式接口和Lambda表达式实现对字符串的处理功能。

首先,我们定义一个函数式接口MyFunc,如下所示。

@FunctionalInterface
public interface MyFunc <T> {
    public T getValue(T t);
}
1
2
3
4

接下来,我们定义一个操作字符串的方法,其中参数为MyFunc接口实例和需要转换的字符串。

public String handlerString(MyFunc<String> myFunc, String str){
    return myFunc.getValue(str);
}
1
2
3

接下来,我们对自定义的函数式接口进行测试,此时我们传递的函数式接口的参数为Lambda表达式,并且将字符串转化为大写。

@Test
public void test6(){
    String str = handlerString((s) -> s.toUpperCase(), "binghe");
    System.out.println(str);
}
1
2
3
4
5

运行test6方法,得出的结果信息如下所示。

BINGHE
1

我们也可以截取字符串的某一部分,如下所示。

@Test
public void test7(){
    String str = handlerString((s) -> s.substring(0,4), "binghe");
    System.out.println(str);
}
1
2
3
4
5

运行test7方法,得出的结果信息如下所示。

bing
1

可以看到,我们可以通过handlerString(MyFunc<String> myFunc, String str)方法结合Lambda表达式对字符串进行任意操作。

注意:作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递,接收Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型 。

# 星球服务

加入星球,你将获得:

1.项目学习:微服务入门必备的SpringCloud Alibaba实战项目、手写RPC项目—所有大厂都需要的项目【含上百个经典面试题】、深度解析Spring6核心技术—只要学习Java就必须深度掌握的框架【含数十个经典思考题】、Seckill秒杀系统项目—进大厂必备高并发、高性能和高可用技能。

2.框架源码:手写RPC项目—所有大厂都需要的项目【含上百个经典面试题】、深度解析Spring6核心技术—只要学习Java就必须深度掌握的框架【含数十个经典思考题】。

3.硬核技术:深入理解高并发系列(全册)、深入理解JVM系列(全册)、深入浅出Java设计模式(全册)、MySQL核心知识(全册)。

4.技术小册:深入理解高并发编程(第1版)、深入理解高并发编程(第2版)、从零开始手写RPC框架、SpringCloud Alibaba实战、冰河的渗透实战笔记、MySQL核心知识手册、Spring IOC核心技术、Nginx核心技术、面经手册等。

5.技术与就业指导:提供相关就业辅导和未来发展指引,冰河从初级程序员不断沉淀,成长,突破,一路成长为互联网资深技术专家,相信我的经历和经验对你有所帮助。

冰河的知识星球是一个简单、干净、纯粹交流技术的星球,不吹水,目前加入享5折优惠,价值远超门票。加入星球的用户,记得添加冰河微信:hacker_binghe,冰河拉你进星球专属VIP交流群。

# 星球重磅福利

跟冰河一起从根本上提升自己的技术能力,架构思维和设计思路,以及突破自身职场瓶颈,冰河特推出重大优惠活动,扫码领券进行星球,直接立减149元,相当于5折, 这已经是星球最大优惠力度!


领券加入星球,跟冰河一起学习《SpringCloud Alibaba实战》、《手撸RPC专栏》和《Spring6核心技术》,更有已经上新的《大规模分布式Seckill秒杀系统》,从零开始介绍原理、设计架构、手撸代码。后续更有硬核中间件项目和业务项目,而这些都是你升职加薪必备的基础技能。

100多元就能学这么多硬核技术、中间件项目和大厂秒杀系统,如果是我,我会买他个终身会员!

# 其他方式加入星球

特别提醒: 苹果用户进圈或续费,请加微信 hacker_binghe 扫二维码,或者去公众号 冰河技术 回复 星球 扫二维码加入星球。

# 星球规划

后续冰河还会在星球更新大规模中间件项目和深度剖析核心技术的专栏,目前已经规划的专栏如下所示。

# 中间件项目

  • 《大规模分布式定时调度中间件项目实战(非Demo)》:全程手撸代码。
  • 《大规模分布式IM(即时通讯)项目实战(非Demo)》:全程手撸代码。
  • 《大规模分布式网关项目实战(非Demo)》:全程手撸代码。
  • 《手写Redis》:全程手撸代码。
  • 《手写JVM》全程手撸代码。

# 超硬核项目

  • 《从零落地秒杀系统项目》:全程手撸代码,在阿里云实现压测(已上新)。
  • 《大规模电商系统商品详情页项目》:全程手撸代码,在阿里云实现压测。
  • 其他待规划的实战项目,小伙伴们也可以提一些自己想学的,想一起手撸的实战项目。。。

既然星球规划了这么多内容,那么肯定就会有小伙伴们提出疑问:这么多内容,能更新完吗?我的回答就是:一个个攻破呗,咱这星球干就干真实中间件项目,剖析硬核技术和项目,不做Demo。初衷就是能够让小伙伴们学到真正的核心技术,不再只是简单的做CRUD开发。所以,每个专栏都会是硬核内容,像《SpringCloud Alibaba实战》、《手撸RPC专栏》和《Spring6核心技术》就是很好的示例。后续的专栏只会比这些更加硬核,杜绝Demo开发。

小伙伴们跟着冰河认真学习,多动手,多思考,多分析,多总结,有问题及时在星球提问,相信在技术层面,都会有所提高。将学到的知识和技术及时运用到实际的工作当中,学以致用。星球中不少小伙伴都成为了公司的核心技术骨干,实现了升职加薪的目标。

# 联系冰河

# 加群交流

本群的宗旨是给大家提供一个良好的技术学习交流平台,所以杜绝一切广告!由于微信群人满 100 之后无法加入,请扫描下方二维码先添加作者 “冰河” 微信(hacker_binghe),备注:星球编号

冰河微信

# 公众号

分享各种编程语言、开发技术、分布式与微服务架构、分布式数据库、分布式事务、云原生、大数据与云计算技术和渗透技术。另外,还会分享各种面试题和面试技巧。内容在 冰河技术 微信公众号首发,强烈建议大家关注。

公众号:冰河技术

# 视频号

定期分享各种编程语言、开发技术、分布式与微服务架构、分布式数据库、分布式事务、云原生、大数据与云计算技术和渗透技术。另外,还会分享各种面试题和面试技巧。

视频号:冰河技术

# 星球

加入星球 冰河技术 (opens new window),可以获得本站点所有学习内容的指导与帮助。如果你遇到不能独立解决的问题,也可以添加冰河的微信:hacker_binghe, 我们一起沟通交流。另外,在星球中不只能学到实用的硬核技术,还能学习实战项目

关注 冰河技术 (opens new window)公众号,回复 星球 可以获取入场优惠券。

知识星球:冰河技术