博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java8新特性-Lambda表达式的详解(从0开始)
阅读量:4036 次
发布时间:2019-05-24

本文共 5612 字,大约阅读时间需要 18 分钟。

这几天复习了java8的一些新特性,作为一个从java5以来最具革命性的版本,一直没有来得及总结。本系列文章主要是从《java8实战》总结的。这是第一篇文章主要介绍java8的lambda。

一、为什么要使用lambda表达式

如果之前见到的话都会觉得,lambda就是一个匿名函数,我们可以这样来理解就好了,Lambda表达式就是为了使得我们的代码更加的简洁。如何简洁呢?我们直接举个例子来看看:

public class LambdaTest1 {
@Test public void test1() {
//第一种 Runnable runnable = new Runnable() {
public void run() {
System.out.println("不使用Lambda表达式"); } }; runnable.run(); System.out.println("======================="); //第二种 Runnable runnable1 = () -> System.out.println("使用Lambda表达式"); runnable1.run(); }}/*不使用Lambda表达式=======================使用Lambda表达式*/

之前我们新建一个线程使用5行代码,但是如果我们使用lambda表达式只需要1行代码即可,是不是很方便。既然这么牛,下面就来看看Lambda表达式如何使用。

二、lambda表达式的使用

1、基本语法

在上面的例子中我们使用了这样一行() -> System.out.println(“使用Lambda表达式”);下面我们对lambda的格式进行一个介绍:

(1)左边括号:lambda的形参列表,就好比是我们定义一个接口,里面有一个抽象方法,这个抽象方法的形参列表。

(2)箭头:lambda的操作符,所以你看见这个箭头心中知道这是一个lambda表达式就可以了。

(3)右边lambda体:就好比是我们实现了接口中的抽象方法。

lambda表达式的使用可以分为以下5种基本的情况。我们一个一个来介绍。

2、无参无返回值

这个是最简单的一种情况,就是刚刚我们所举的例子。为了不混淆我们再举一个例子。

//此时如果方法体比较复杂好几行代码,那么这个{}是不能省略的Runnable runnable1 = () -> {
System.out.println("使用Lambda表达式"); System.out.println("使用Lambda表达式");};

我们可以看到没有任何参数也没有任何返回值,因此可以直接写,不过lambda体如果不是一行代码,那么就需要使用{}将其括起来。

3、有参数无返回值

@Test    public void test2() {
//第一种:没有使用lambda表达式 Consumer
consumer = new Consumer
() {
@Override public void accept(String s) {
System.out.println(s); } }; consumer.accept("没有使用lambda:有参数,但是没有返回值"); //第二种:使用lambda表达式 Consumer
consumer1 = (String s)->{
//此时只有一行输出代码,因此可以省去外部的{} System.out.println(s); }; consumer.accept("使用lambda:有参数,但是没有返回值"); }

我们看这个例子,只接受String参数,但是缺没有返回值。

4、有参数无返回值,数据类型可省略,称为类型推断

这种情况只能称之为上面的一种特例,只不过我们可以不传入类型,由编译器帮我们推断出来即可。

Consumer
consumer1 = (s)->{
//此时只有一行输出代码,因此可以省去外部的{} System.out.println(s);};consumer.accept("使用lambda:有参数,但是没有返回值");

在这个例子中我们可以看到,直接把s中的String类型给去掉了,此时运行依然是正确的。这就是编译器自动为我们推断出了s的类型就是String的。可能你会对类型推断有点疑惑,不知道是啥,不过下面举出几个例子相信你应该明白了

//右边的new ArrayList<>()如果没有类型推断应该是//new ArrayList
()ArrayList
list = new ArrayList<>();//{}默认就是StringString[] list2= {
};int[] arr = {
};

相信现在大家应该能理解了吧。对了还有一点,那就是如果只有一个参数的时候可以直接把s的小括号去掉,有多个参数时候不可以。

5、有多个参数,有返回值

@Test    public void test3() {
//第一种:没有使用lambda表达式 Comparator
comparator = new Comparator
() {
@Override public int compare(Integer o1, Integer o2) {
System.out.println("o1:"+o1); return o1.compareTo(o2); } }; System.out.println(comparator.compare(1,2)); System.out.println("======================"); //第二种:使用lambda表达式 Comparator
comparator2 = (o1,o2)->{
System.out.println("o1:"+o1); return o1.compareTo(o2); }; System.out.println(comparator2.compare(1,2)); }

我们使用了一个比较器,当然了如果只有一条return语句的话,那样式就更简单了。箭头直接指向我们要返回的结果。

@Test    public void test3() {
//第一种:没有使用lambda表达式 Comparator
comparator = new Comparator
() {
@Override public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2); } }; System.out.println(comparator.compare(1,2)); System.out.println("======================"); //第二种:使用lambda表达式 Comparator
comparator2 = (o1,o2)-> o1.compareTo(o2); System.out.println(comparator2.compare(1,2)); }

这样就把基本的语法介绍完了,如果你只是想认识一下如何使用,下面的就可以不用看了,但是学习嘛,都是往深处钻。下面就深入解析一下:

三、Lambda表达式深入解析

想要对lambda表达式有一个深入的理解,我们需要去认识另外一个知识点,那就是函数式接口。在上面我们的举得例子中比如Consumer或者是Comparator为什么能够使用lambda呢?就是因为实函数式接口,下面我们来认识一下:

1、什么是函数式接口

比如我们的Runnable就是一个函数式接口,我们可以到源码中看看:

@FunctionalInterfacepublic interface Runnable {
/** * When an object implementing interface Runnable is used * to create a thread, starting the thread causes the object's * run method to be called in that separately executing * thread. *

* The general contract of the method run is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run();}

他主要有如下的特点:

(1)含有@FunctionalInterface注解

(2)只有一个抽象方法

**也就是说只有函数式接口的变量或者是函数式接口,才能够赋值为Lambda表达式。**当然了方法的类型可以任意。

2、函数式接口有什么用呢?

函数式接口能够接受匿名内部类的实例化对象,换句话说,我们可以使用匿名内部类来实例化函数式接口的对象,而Lambda表达式能够代替内部类实现代码的进一步简化。并且java为我们提供了四个比较重要的函数式接口:

  1. 消费型接口:Consumer< T> void accept(T t)有参数,无返回值的抽象方法;
  2. 供给型接口:Supplier < T> T get() 无参有返回值的抽象方法;
  3. 断定型接口: Predicate< T> boolean test(T t):有参,但是返回值类型是固定的boolean
  4. 函数型接口: Function< T,R> R apply(T t)有参有返回值的抽象方法;

这里仅仅是给出了4个,其实java提供了很多。比如java.util.function包下还有很多函数式接口可供使用。

3、自定义一个函数式接口

@FunctionalInterfacepublic  interface MyInterface{
void test();}public class LambdaTest2 {
public static void main(String[] args) {
MyInterface myInterface = () -> System.out.println("test"); }}

现在我们定义了一个MyInterface的函数式接口,里面定义了一个test方法,如果我们定义了两个就不能使用lambda表达式了,为什么呢?因为lambda是一个接口方法,如果有两个方法,应该指定哪一个呢?就搞混了。

4、类型推导

在第二部分介绍lambda语法的时候曾经说过,lambda本身具有类型推导,那么这个类型推导可以做到什么程度呢?编译器负责推导lambda的类型,它利用上下文被期待的类型当做推导的目标类型,当满足下面条件时,就会被赋予目标类型:

(1)被期待的目标类型是一个函数式接口

(2)lambda的入参类型和数量与该接口一致

(3)返回类型一致

(4)抛出异常类型一致

其实**lambda最后会由编译器生成static 方法在当前类中,利用了invokedynamic命令脱离了内部类实现的优化。**这一部分可以参考汪文军大佬的视频。

OK,现在相信你对lambda有了一个更加深刻的认识。我在网上搜索了一下关于lambda表达式的面试题基本上都是基于其语法使用和上面的这几个点。

喜欢的可以关注我的微信公众号:java的架构师技术栈,。获取更多文章和教程资源

转载地址:http://isbdi.baihongyu.com/

你可能感兴趣的文章
No.176 - LeetCode1309
查看>>
No.182 - LeetCode1325 - C指针的魅力
查看>>
mysql:sql create database新建utf8mb4 数据库
查看>>
mysql:sql alter database修改数据库字符集
查看>>
mysql:sql drop table (删除表)
查看>>
mysql:sql truncate (清除表数据)
查看>>
scrapy:xpath string(.)非常注意问题
查看>>
yuv to rgb 转换失败呀。天呀。谁来帮帮我呀。
查看>>
yuv420 format
查看>>
YUV420只绘制Y通道
查看>>
yuv420 还原为RGB图像
查看>>
LED恒流驱动芯片
查看>>
驱动TFT要SDRAM做为显示缓存
查看>>
使用file查看可执行文件的平台性,x86 or arm ?
查看>>
qt5 everywhere 编译summary
查看>>
qt5 everywhere编译完成后,找不到qmake
查看>>
arm-linux开机读取硬件时钟,设置系统时钟。
查看>>
交叉编译在x86上调试好的qt程序
查看>>
qt 创建异形窗体
查看>>
可重入函数与不可重入函数
查看>>