前言

Lambda表达式是JDK8的一个新特性,可以取代大部分的匿名内部类,写出更优雅的Java代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构

JDK也提供了大量的内置函数式接口供我们使用,使得Lambda表达式的运用更加方便、高效

一、什么是Lambda表达式

Lambda表达式,也称为闭包java8的新特性,lambda运行将函数作为一个方法的参数,也就是将函数作为参数传递到方法中。使用lambda表达式可以让代码更加简洁。

Lambda表达式常用于简化接口实现,关于接口实现,可以有很多种方式。例如:

  1. 创建接口的实现类;
  2. 使用匿名内部类;

但是lambda表达式,比这两种方式都简单。代码示例如下:

interface TestInterface{public void testFun();
}public class TestClass {public static void main(String[] args) { //使用lambda表达式实现接口TestInterface test = () -> {System.out.println("test");};test.testFun();}
}

二、使用前提

上文中提到,lambda表达式可以在⼀定程度上简化接口的实现。但是,并不是所有的接口都可以使用lambda表达式来简化接口的实现的。

先说结论,lambda表达式,只能实现函数式接口lambda表达式毕竟只是⼀个匿名方法

三、函数式接口

3.1 概念

函数式接口在 Java 中是指: 有且仅有一个抽象方法的接口

函数式接口,即适用于函数式编程场景的接口。而 Java 中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的 Lambda才能顺利地进行推导。

备注:

语法糖 是指使用更加方便,但是原理不变的代码语法。

例如:

在遍历集合时使用的for-each语法,其实底层的实现原理仍然是迭代器,这便是语法糖。从应用层面来讲, Java 中的 Lambda 可以被当做是匿名内部类的语法糖,但是二者在原理上是不同的。

3.2 格式

只要确保接口中有且仅有一个抽象方法即可,伪代码如下:

修饰符 interface 接口名称 {public abstract 返回值类型 方法名称(可选参数信息);// 其它非抽象方法内容
}

由于接口当中抽象方法的public abstract是可以省略的,所以定义一个函数式接口很简单:

public interface TestFunctionalInterface {void testMethod();
}

3.3 @FunctionalInterface

@Override 注解的作用类似, Java 8 中专门为函数式接口引入了一个新的注解:@FunctionalInterface。该注解可用于一个接口的定义上:

@FunctionalInterface
public interface TestFunctionalInterface {void testMethod();
}

一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。需要注意的是,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。

3.4 自定义函数式接口

对于刚刚定义好的 MyFunctionalInterface 函数式接口,典型使用场景就是作为方法的参数:

public class TestFunctionalClass {// 使用自定义的函数式接口作为方法参数private static void doSomeThing(TestFunctionalInterface testInterFace){testInterFace.testMethod();}public static void main(String[] args) {// 调用函数式接口的方法doSomeThing(() -> System.out.println("Hello world!"));}}

四 语法格式

4.1 基础语法

lambda表达式,其实本质来讲,就是⼀个匿名函数。因此在写lambda表达式的时候,不需要关心方法名是什么。

实际上,我们在写lambda表达式的时候,也不需要关心返回值类型,只需要关注两部分内容即可:参数列表和方法体

(参数1,参数2,…) -> {

​ 方法体

};

各部分详述

参数部分:方法的参数列表,要求和实现的接口中的方法参数部分⼀致,包括参数的数量和类型。

方法体部分 : 方法的实现部分,如果接口中定义的方法有返回值,则在实现的时候,注意返回值的返回。

-> : 分隔参数部分和方法体部分。

总结来说:语法形式为 () -> {},其中 () 用来描述参数列表,{} 用来描述方法体,-> 为 lambda运算符 ,读作(goes to)。

4.2 重要特征

  • **可选类型声明:**不需要声明参数类型,编译器可以统一识别参数值。
  • **可选的参数圆括号:**一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • **可选的大括号:**如果主体包含了一个语句,就不需要使用大括号。
  • **可选的返回关键字:**如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。

代码示例:

public class Demo01 {/**多参数无返回*/@FunctionalInterfacepublic interface NoReturnMultiParam {void method(int a, int b);}/**无参无返回值*/@FunctionalInterfacepublic interface NoReturnNoParam {void method();}/**一个参数无返回*/@FunctionalInterfacepublic interface NoReturnOneParam {void method(int a);}/**多个参数有返回值*/@FunctionalInterfacepublic interface ReturnMultiParam {int method(int a, int b);}/*** 无参有返回*/@FunctionalInterfacepublic interface ReturnNoParam {int method();}/**一个参数有返回值*/@FunctionalInterfacepublic interface ReturnOneParam {int method(int a);}public static void main(String[] args) {//无参无返回NoReturnNoParam noReturnNoParam = () -> {System.out.println("NoReturnNoParam");};noReturnNoParam.method();//一个参数无返回NoReturnOneParam noReturnOneParam = (int a) -> {System.out.println("NoReturnOneParam param:" + a);};noReturnOneParam.method(6);//多个参数无返回NoReturnMultiParam noReturnMultiParam = (int a, int b) -> {System.out.println("NoReturnMultiParam param:" + "{" + a +"," + + b +"}");};noReturnMultiParam.method(6, 8);//无参有返回值ReturnNoParam returnNoParam = () -> {System.out.print("ReturnNoParam");return 1;};int res = returnNoParam.method();System.out.println("return:" + res);//一个参数有返回值ReturnOneParam returnOneParam = (int a) -> {System.out.println("ReturnOneParam param:" + a);return 1;};int res2 = returnOneParam.method(6);System.out.println("return:" + res2);//多个参数有返回值ReturnMultiParam returnMultiParam = (int a, int b) -> {System.out.println("ReturnMultiParam param:" + "{" + a + "," + b +"}");return 1;};int res3 = returnMultiParam.method(6, 8);System.out.println("return:" + res3);}}

控制台输出:

NoReturnNoParam
NoReturnOneParam param:6
NoReturnMultiParam param:{6,8}
ReturnNoParamreturn:1
ReturnOneParam param:6
return:1
ReturnMultiParam param:{6,8}
return:1

五 语法简化

5.1 参数部分的精简

5.1.1 参数的类型

由于在接口的方法中,已经定义了每⼀个参数的类型是什么。而且在使用lambda表达式实现接口的时候,必须要保证参数的数量和类 型需要和接口中的方法保持⼀致。因此,此时lambda表达式中的参数的类型可以省略不写。

注意事项:

如果需要省略参数的类型,要保证:要省略, 每⼀个参数的类型都必须省略不写。绝对不能出现,有的参数类型省略了,有的参数类型没有省略。

// 有参+返回值
Test test = (name,age)  -> {System.out.println(name+age+"了!");return age + 1;
};int age = test.test("小刘学编程",18);
System.out.println(age);

5.1.2 参数的小括号

如果方法的参数列表中的参数数量 有且只有⼀个,此时,参数列表的小括号是可以省略不写的。

注意事项:

  • 只有当参数的数量是⼀个的时候, 多了、少了都不能省略。
  • 省略掉小括号的同时, 必须要省略参数的类型
//一个参数
Test test = name -> {System.out.println(name+"test");
};
test.test("小刘学编程");

5.2 方法体部分的精简

当⼀个方法体中的逻辑,有且只有⼀句的情况下,⼤括号可以省略

Test test = name -> System.out.println(name+"test");
test.test("小新");

5.3 return部分的精简

如果⼀个方法中唯⼀的⼀条语句是⼀个返回语句, 此时在省略掉大括号的同时, 也必须省略掉return。

Test test = (a,b) -> a+b;

六、常用示例

lambda表达式是为了简化接口的实现的,在lambda表达式中,不应该出现比较复杂的逻辑。如果在lambda表达式中需要处理的逻辑比较复杂,会对程序的可读性造成非常大的影响,⼀般情况会单独的写⼀个方法。在lambda表达式中直接引用这个方法即可。

函数引用:引用⼀个已经存在的方法,使其替代lambda表达式完成接口的实现

6.1 静态方法的引用

语法:类::静态方法

注意事项:

  • 在引用的方法后面,不要添加小括号。
  • 引用的这个方法,参数(数量、类型)和返回值,必须要跟接口中定义的⼀致
class Subtraction{public static int subtract(int a,int b ){// 稍微复杂的逻辑:计算a和b的差值的绝对值if (a > b) {return a - b;}return b - a;}
}interface TestInterface{int test(int a,int b);
}public class TestClass {public static void main(String[] args) {//实现多个参数,一个返回值的接口//对一个静态方法的引用,语法:类::静态方法TestInterface test = Subtraction::subtract;System.out.println(test.test(1,2));}
}

6.2 非静态方法的引用

语法:方法归属者::方法名 静态方法的归属者为类名,普通方法归属者为对象

注意事项:

  • 在引用的方法后⾯,不要添加小括号。
  • 引用的这个方法, 参数(数量、类型) 和 返回值, 必须要跟接口中定义的⼀致。
public class Test06 {public static void main(String[] args) {//对非静态方法的引用,需要使用对象来完成Test2 test2 = new Calculator()::calculate;System.out.println(test2.calculate(2, 3));}private static class Calculator{public int calculate(int a, int b) {return a > b ? a - b : b - a;}}
}
interface Test2{int calculate(int a,int b);
}

6.3 构造方法的引用

使用场景

如果某⼀个函数式接口中定义的方法,仅仅是为了得到⼀个类的对象。此时我们就可以使用构造方法的引用,简化这个方法的实现。

语法:类名::new

注意事项:可以通过接口中的方法的参数, 区分引用不同的构造方法。

interface ItemCreatorBlankConstruct {Item getItem();
}
interface ItemCreatorParamContruct {Item getItem(int id, String name, double price);
}public class Exe2 {public static void main(String[] args) {ItemCreatorBlankConstruct creator = () -> new Item();Item item = creator.getItem();ItemCreatorBlankConstruct creator2 = Item::new;Item item2 = creator2.getItem();ItemCreatorParamContruct creator3 = Item::new;Item item3 = creator3.getItem(112, "小刘学编程", 135.99);}
}

6.4 Lambda 表达式创建线程

创建线程一般都是通过创建Thread对象,然后通过匿名内部类重写run()方法,一提到匿名内部类就应该想到可以使用 lambda 表达式来简化线程的创建过程。

Thread t = new Thread(() -> {for (int i = 0; i < 10; i++) {System.out.println("新建线程->" + ":" + i);}
});
t.start();

6.5 遍历集合

可以调用集合的 public void forEach(Consumer<? super E> action) 方法,通过 lambda 表达式的方式遍历集合中的元素。以下是Consumer接口的方法以及遍历集合的操作。Consumer接口是jdk提供的一个函数式接口。

@FunctionalInterface
public interface Consumer<T> {void accept(T t);//....
}
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1,2,3,4,5);
//lambda表达式 方法引用
list.forEach(System.out::println);list.forEach(item -> {if (item % 2 == 0) {System.out.println(item);}
});

6.6 删除集合中的某个元素

通过public boolean removeIf(Predicate<? super E> filter)方法来删除集合中的某个元素,Predicate也是jdk为提供的一个函数式接口,可以简化程序的编写。

ArrayList<Item> items = new ArrayList<>();
items.add(new Item(11, "小牙刷", 12.05 ));
items.add(new Item(5, "日本马桶盖", 999.05 ));
items.add(new Item(7, "格力空调", 888.88 ));
items.add(new Item(17, "肥皂", 2.00 ));
items.add(new Item(9, "冰箱", 4200.00 ));items.removeIf(ele -> ele.getId() == 7);//通过 foreach 遍历,查看是否已经删除
items.forEach(System.out::println);

6.7 集合内元素的排序

若要为集合内的元素排序,就必须调用sort方法,传入比较器匿名内部类重写compare 方法,现在可以使用lambda 表达式来简化代码。

ArrayList<Item> list = new ArrayList<>();
list.add(new Item(13, "背心", 7.80));
list.add(new Item(11, "半袖", 37.80));
list.add(new Item(14, "风衣", 139.80));
list.add(new Item(12, "秋裤", 55.33));list.sort((o1, o2) -> o1.getId() - o2.getId());
System.out.println(list);

七、注意

这⾥类似于局部内部类匿名内部类,依然存在闭包的问题。如果在lambda表达式中,使用到了局部变量,那么这个局部变量会被隐式的声明为 final。是⼀个常量,不能修改值。

如下代码示例:如果我们把注释放开会报错,提示num值是final不能被改变。这里虽然没有标识num类型为final,但是在编译期间虚拟机会加上fina修饰关键字。

public static void main(String[] args) {int num = 10;Test<String> test = () -> {System.out.println(num);};//num = num + 2;test.doSomeThing("hello world !");
}

总结

本文从Lambda表达式的基础概念、函数式接口、以及Lambda表达式的常用示例几方面完整的讨论了这一Java8新增的特性,实际开发中确实为我们提供了许多便利,简化了代码。欢迎小伙伴继续提出不同的见解一起讨论!

参考 & 鸣谢

1、Java中Lambda表达式使用及详解

2、Lambda表达式详解

感谢前人的经验、分享和付出,让我们可以有机会站在巨人的肩膀上眺望星辰大海!

「 Java基础-Lambda 」试试Lambda表达式?通俗易懂得嘞相关推荐

  1. java里面value_「Java基础知识」Java中包含哪些运算符

    原标题:「Java基础知识」Java中包含哪些运算符 在Java中包含的运算符有:算数运算符,逻辑运算符,关系运算符等. 算数运算符也就是我们平时的加减乘除余等操作:在Java中都是将右边的值赋值给左 ...

  2. java里面string什么意思_「Java基础知识」Java中的字符串是什么

    原标题:「Java基础知识」Java中的字符串是什么 字符串顾名思义就是一些字符组合在一起组成的一串数据,称作字符串,在Java中字符串用双引号包围起来,格式为String string = &quo ...

  3. java代码规范插件_「Java基础知识」代码规范插件怎么用

    原标题:「Java基础知识」代码规范插件怎么用 在开发中,好的编程风格可以提升团队合作能力,提升开发的效率,但是每个人都有自己的编程习惯,如何能够将大家的编程风格统一,这个在团队中也很重要; 在Jav ...

  4. mongodb模糊查询_我叫Mongo,收了「查询基础篇」,值得你拥有

    这是mongo第二篇「查询基础篇」,后续会连续更新6篇 mongodb的文章总结上会有一系列的文章,顺序是先学会怎么用,在学会怎么用好,戒急戒躁,循序渐进,跟着我一起来探索交流. 通过上一篇基础篇的介 ...

  5. 「 Java开发规范 」10人小团队Java开发规范参考这篇就够了

    <菜鸟程序员成长计划>之团队高效合作[开发规范篇] 1.「 Java开发规范 」10人小团队Java开发规范参考这篇就够了! 2.「 前端开发规范 」10人小团队前端开发规范参考这篇就够了 ...

  6. python程序如何执行死刑_「Python基础知识」Python生成器函数

    原标题:「Python基础知识」Python生成器函数 对于程序而言,内存也是很重要的,因为程序中很多数据都是保存在内存中的,如果内存中存储的数据过多,那么系统就会崩溃,这是人们不希望发生的. 可以采 ...

  7. linux 筛选文件,「Linux基础知识」grep文件内容筛选命令的使用

    原标题:「Linux基础知识」grep文件内容筛选命令的使用 grep命令用于从文档中抓取显示包含指定字符的行,grep命令的使用格式如下: grep [选项] 匹配模式 文件1 文件2 ...... ...

  8. Java基础之泛型简单讲解(通俗易懂)

    Java基础之泛型简单讲解(通俗易懂) 1. 前言 2. 简单例子对比理解 2.1 未使用泛型例子--ArrayList 2.2 使用泛型的例子 2.2.1 ArrayList 举例 2.2.2 Ha ...

  9. java string 占位符_驳《阿里「Java开发手册」中的1个bug》?

    前两天写了一篇关于<阿里Java开发手册中的 1 个bug>的文章,评论区有点炸锅了,基本分为两派,支持老王的和质疑老王的. 首先来说,无论是那一方,我都真诚的感谢你们.特别是「二师兄」, ...

最新文章

  1. JBPM4.4_jBPM4.4应用(与Spring集成自行控制事务等)
  2. 排查指南 | 两个案例学会从埋点排查 iOS 离线包
  3. python之setdefault()和defaultdict()处理缺失值的键
  4. 吴恩达机器学习练习4:神经网络学习(反向传播)
  5. mysql怎么约束_MySQL 约束详解
  6. CSS3学习案例1——超级链接类型标示图标
  7. 暴风影音 去广告 方法
  8. 同济版《线性代数》再遭口诛笔伐,网友:它真的不太行
  9. c++输出字符串结尾部分中文乱码问题
  10. 五款免费pdf转换成word转换器软件下载
  11. 干货 | 产品经理如何画脑图
  12. 道路匹配MapMatching:GPS轨迹点常用聚类算法介绍(K-Means聚类、蚁群算法等)
  13. SOAP Client in Delphi7
  14. 苹果超省电技巧分享,教你如何玩转你的iPhone 手机
  15. 正交变换(傅里叶变换、Z变换)
  16. Ruby学习入门简介
  17. linux 系统加载优盘
  18. 世界地图自定义绘制结合echarts,实现点击介绍项目
  19. Ruby编程语言(The Ruby Programming Language) 中文版
  20. R大多年前的一篇老PPT,找不到原文了

热门文章

  1. 【MFC编程】MFC将ListCtrl列表导出到Excel-采用文件流写Excel兼容Html格式(不需要Excel环境)
  2. tomcat使用cronolog进行日志切割
  3. 【转】架构师成长历程:行到水穷处,坐看云起时
  4. EndNote参考文献管理
  5. 金盾金狮点盾云鹏保宝等加密视频录屏翻录提取教程
  6. 模块说和神经网络学说_2021心理学考研背诵题:脑学说
  7. 基于GPIB总线的无线电频谱监测系统的设计delphi多线程超时控
  8. 小游戏市场被引爆,如何利用才能正确解锁?
  9. 计算机仿真的过程,计算机仿真的过程与方法
  10. 解决“下载软件仓库信息失败 检查您的网络连接”仓库 “http://ppa.launchpad.net/fcitx-team/..../”没有Release文件