Java8 引入 Lambda 表达式,允许开发者将函数当成参数传递给某个方法,或者把代码本身当作数据进行处理。使用 Lambda 表达式,使得应用变得简洁而紧凑。 很多语言(Groovy、Scala 等)从设计之初就支持 Lambda 表达式。但是 java 中使用的是匿名内部类代替。最后借助强大的社区力量,找了一个折中的 Lambda 实现方案,可以实现简洁而紧凑的语言结构。

一、匿名内部类到Lambda的演化

匿名内部类,即一个没有名字的,存在于一个类或方法内部的类。当我们需要用某个类且只需要用一次,创建和使用合二为一时,我们可以选择匿名内部类,省掉我们定义类的步骤。

匿名内部类会隐士的继承一个类或实现一个接口,或者说匿名内部类是一个继承了该类或者实现了该接口的子类匿名对象。下面看一个匿名内部类的例子package com.java8;

/*

定义和使用匿名内部类

*/

public class NoNameClass {

public static void main(String[] args) {

Model m = new Model(){

@Override

public void func() {

System.out.println("方法的实现");

}

};

m.func();

}

}

// 需要被实现的接口

interface Model{

void func();

}

等价的 Lambda 代码package com.java8;

/*

定义和使用Lambda 简化代码

*/

public class NoNameClass {

public static void main(String[] args) {

Model m = new Model(){()->{

System.out.println("方法的实现");

}};

m.func();

}

}

可以看出使用 Lambda 表达式替代了匿名内部类代码,使得代码更加简化、紧凑。

二、语法

(parameters) -> expression 或 (parameters) ->{ statements; }可选类型声明

不需要声明参数类型,编译器可以统一识别参数值。

可选的参数圆括号

一个参数无需定义圆括号,但多个参数需要定义圆括号。

可选的大括号

如果主体包含了一个语句,就不需要使用大括号。

可选的返回关键字

如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指明表达式返回了一个数值。

Lambda 表达式示例:

三、Lambda使用形式

使用 Lambda 时,实现方法可以有参数,也可以有返回值,如果没指定参数类型,则由编译器自行推断得出。

1、无参带返回值

生成[1,10]之间的任意整数interface Model2{

int func();

}

Model2 md2 = () -> {return (int)(Math.random()*10+1)};

Lambda 的改写需要有对应的抽象方法,当没有参数时需要使用()占位,当表达式只有一行代码时,可以省略return和{}

以上的 Lambda 等价于:Model2 md2 = () -> (int)(Math.random()*10+1);

2、带参带返回值

返回一个对数字描述的字符串。interface Model3{

String func(int a);

}

Model3 md3 = (int a) -> {

return "This is a number " + a;

};

形参写在()内即可,参数的类型可以省略,此时将由编译器自行推断得出,同时还可以省略()

以上的 Lambda 等价于:md3 = a -> "This is a number " + a;

省略了参数类型,小括号,同时连带实现体的括号和 return 一并省去。

3、带多个参数

根据输入的运算符计算两个数的运算,并返回结果interface Model4{

String func(int a, int b, String oper);

}

Model4 md4 = (a, b, s) -> {

String res = "";

if("+".equals(s)){

res = ( a+b ) + "";

}else if("-".equals(s)){

res = ( a-b ) + "";

}else if("*".equals(s)){

res = ( a*b ) + "";

}else if("/".equals(s)){

res = ( a/b ) + ""; // 暂不考虑除0的情况

}else{

res = "操作有失误";

}

return res;

};

System.out.println(md4.func(1,1,"+"));

以上例子为多个参数的 Lambda 表达式,其中省略掉了每一个参数的类型,编译器自动推断。多条语句时实现体的{}不能省。

四、Lambda作为参数

在 Java8 之前,接口可以作为方法参数传入,执行时必须提供接口实现类的实例。从 java8 开始,Lambda 可以作为接口方法实现,当作参数传入,无论从形式上还是实际上都省去了对象的创建。使代码更加的紧凑简单高效。

定义接口

在接口中,必须有且仅有一个抽象方法,以确定 Lambda 模板// 无参无返回值的方法

interface LambdaInterface1{

void printString();

}

// 带参无返回值的方法

interface LambdaInterface2{

void printString(String str);

}

定义方法接收参数

在某方法中需要使用接口作为参数// 无参

public static void testLambda(LambdaInterface1 lam1){

lam1.printString();

}

// 带参

public static void testLambda2(String s,LambdaInterface2 lam2){

lam2.printString(s);

}

Lambda表达式作为参数传入// 无参Lambda作为参数

testLambda(()->{

System.out.println("可以简单,可以复杂");

});

// 带参Lambda作为参数

testLambdaParam("hello",(a)->{

System.out.println(a);

});

五、Lambda中使用变量

在 Lambda 中可以定义自己的局部变量,也可以使用外层方法的局部变量,还可以使用属性。这一点也不难理解,既然是一个方法的实现,只写了一个代码块,那么使用本身所属方法的局部变量和类的属性也并不过分。public static void main(String[] args) {

List strs = new ArrayList(){

{

add("aaa");

add("bbb");

add("ccc");

}

};

int j = 1;

strs.forEach((str)->{

int i = 0;

System.out.println(str + " " + i + " " + j);

});

}

六、Lambda类型推断

类型检查

Lambda 的类型是从使用 Lambda 的上下文推断出来的。 Lambda 表达式的参数与函数式接口内方法的参数,返回值类型相互对应。Lambda 表达式需要的类型,或者说 Lambda 实现的那个函数式接口称之为目标类型。

类型推断

利用目标类型来检查一个 Lambda 是否可以用于某个特定的上下文,推断 Lambda 参数的类型。

七、Lambda表达式实战

1、热销商品排序

排序对于久经开发的你来说可能并不陌生,假如原来你做过电商项目,相信对于电商场景下的商品记录排序操作很有感情,下面我们使用Lambda 来看看热销商品排序的操作。

测试数据这里以手机测试数据为例/**

* 实际开发数据通常从数据库获取

* 这里使用测试数据

*/

Goods g01=new Goods(1,"小米9",1789,200, BigDecimal.valueOf(2500));

Goods g02=new Goods(2,"华为Mate20",5000,3000, BigDecimal.valueOf(7000));

Goods g03=new Goods(3,"OPPO R17",2000,2827, BigDecimal.valueOf(1500));

Goods g04=new Goods(4,"魅族 Note9",2000,1600, BigDecimal.valueOf(1600));

Goods g05=new Goods(5,"一加6T",8000,5000, BigDecimal.valueOf(3500));

List goods= Arrays.asList(g01,g02,g03,g04,g05);

Collections.sort 静态方法实现排序Collections.sort(goods,(g1,g2)->g1.getSale()-g2.getSale());

List.sort 默认方法实现集合排序

// 使用 Lambda 对商品记录按销量进行排序

goods.sort((g1,g2)->g1.getSale()-g2.getSale());

Stream.sorted 方法实现元素排序// 多个条件排序情况 Lambda 配置Stream 销量+价格排序 销量相等时按照价格排序

goods =goods.stream().sorted((g1,g2)->g1.getSale()-g2.getSale())

.sorted((g1,g2)->g1.getPrice().compareTo(g2.getPrice()))

.collect(Collectors.toList());

2、日志输出优化

对于项目开发日志打印是一项不可获取的模块,无论是在开发阶段还是项目部署上线后,日志信息的输出对于开发人员来以及运维人员来说都是一项重要的参考指标。

日志输出场景这里以用户模块 UserService 为例,以下为优化前的日志输出代码:public String login(String userName, String userPwd) {

logger.info("UserService 接收到参数-->" + userName + "," + userPwd);

/**

* 登录逻辑省略

*/

return "login";

}

日志级别设置到 debug,在开发阶段方便查看后端接收到的参数信息。仔细分析这里的日志代码,可以看到当日志级别设置为 info 时 debug 日志不应该执行输出操作,同时这里调用 debug 方法时,对于传入的字符串参数需要作对应的拼接操作,才会传入过来。当访问的情况在商城项目做活动情况下 这里的情况有可能会变得很糟糕:所有的 debug 信息全部输出 同时会有大量字符串拼接操作,会影响整个应用程序的执行性能。

日志输出场景这里以用户模块 UserService 为例,日志输出代码优化输出日志前判断日志输出级别

借助 Lambda 延迟日志内容输出/**

* 添加info方法

* 判断日志打印级别

* 当条件成立时 输出日志信息

* @param logger

* @param message

*/

public void info(Log logger, Supplier message){

if(logger.isInfoEnabled()){

logger.info(message.get());

}

}

public String login(String userName, String userPwd) {

//logger.info("UserService 接收到参数-->" + userName + "," + userPwd);

// 延迟Lambda 表达式执行 只有确定

info(logger,()->"UserService 接收到参数-->" + userName + "," + userPwd);

return "login";

}

八、Lambda优势与使用场景

Lambda 表达式的引入取代了匿名内部类,使得代码变得简洁、紧凑,同时 Lambda 的惰性特点,在开发时能够提高应用程序的执行性能。

对于 Lambda 的应用场景,从代码结构来说通常是结合函数式接口来使用,使得开发是面向函数来进行编程,也是 Java8 引入的一种新的思想-函数式编程(后续会介绍)。同时还会结合前面讲到的接口默认方法提现到应用开发中。

java 8 lamda,Java8中你可能不知道的一些地方之Lambda表达式实战相关推荐

  1. java function void_Java8中你可能不知道的一些地方之函数式接口实战

    什么时候可以使用 Lambda?通常 Lambda 表达式是用在函数式接口上使用的.从 Java8 开始引入了函数式接口,其说明比较简单:函数式接口(Functional Interface)就是一个 ...

  2. java8中class怎么用_Java8中你可能不知道的一些地方之方法引用实战

    Java8 中引入方法引用新特性,用于简化应用对象方法的调用, 方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法. 方法引用提供了一种引用而不执行方法的方式,它需要由兼容的函数式接口构成的 ...

  3. java8实战怎么样_Java8中你可能不知道的一些地方之Stream实战

    说起流,我们会想起手机 ,电脑组装流水线,物流仓库商品包装流水线等等.如果把手机 ,电脑,包裹看做最终结果的话,那么加工商品前的各种零部件就可以看做数据源,而中间一系列的加工作业操作,就可以看做流的处 ...

  4. Java基础之Java8中Map的compute的使用

    Java基础之Java8中Map的compute的使用 一.介绍 Java8更新后,Map接口中提供了compute方法.下面我们先看看官方文档的对它的使用说明. 如果看完上面的还是不太明白的话,看下 ...

  5. Java基础之Java8中map和flatMap的使用

    Java基础之Java8中map和flatMap的使用 一.介绍 首先,看下map和flatMap的官方文档说明 map flatMap 其实单纯的看api说明还是比较抽象,下面我将以几个实战例子来帮 ...

  6. 国际计算机思维比赛,少儿编程领域中,你所不知道的国际计算思维大赛

    原标题:少儿编程领域中,你所不知道的国际计算思维大赛 孩子学习编程,除了培养编程思维和计算思维,也能为未来应对人工智能时代打下基础.今天给大家讲的的是Bebras国际计算思维大赛,请大家持续关注坚果学 ...

  7. java枚举变量带括号_Java~如何使用Enum(枚举)和Lambda表达式

    文章目录 Enum的使用 switch中使用 常见方法 values() 以数组形式返回枚举类型的所有成员 ordinal() 获取枚举成员的索引位置 valueOf() 将普通字符串转换为枚举实例 ...

  8. 【Java从入门到头秃专栏 7】语法篇(六) :Lambda表达式(->) 方法引用(::) stream流

    目录 1 Lambda表达式( -> ) ​ 2 方法引用( :: ) 3 Stream流 接下来介绍的三种语法叫:Lambda表达式 方法引用 stream流,这三种语法的使用要有特定条件,在 ...

  9. java zoneoffset,java - 如何在java8中获取默认的ZoneOffset? - SO中文参考 - www.soinside.com...

    tl;dr OffsetDateTime.now().getOffset() 但您可能应该使用时区而不是仅仅偏离UTC. ZoneId.systemDefault() Offset versus Ti ...

最新文章

  1. Spring整合Struts2
  2. 异步/同步、阻塞/非阻塞的理解
  3. 增加表空间大小的四种方法
  4. C语言实现常用排序算法——基数排序
  5. Java Arrays.sort()函数
  6. libusb获取usb设备的idVendor(vid),idProduct(pid),以及Serial Number
  7. 如何在Python中串联两个列表?
  8. manjaro 安装mysql_manjaro安装mysql5.7
  9. python3.5安装教程及环境配置_Windows7环境下Python2.7与Python3.5共存安装
  10. jquery uploadify 相关文档 按钮样式显示不全 Google浏览器显示问题
  11. HTML5基础网页设计(加代码CSS)
  12. linux 主机管理平台,Linux虚拟主机管理系统directadmin使用中文教程
  13. Android WebView重定向GOBACK问题
  14. B - Learning Languages
  15. 程序员口中常说的API是什么意思?什么是API?
  16. WordPress插件教程-WPS Hide Login更改后台登录地址
  17. ​在职场,我们如果高效地沟通
  18. 一键反推食谱,与狗对话,识别女票表情…愚人节,AI 也被玩坏了
  19. “全渠道”难道又是电商对实体店的一场“新骗局”?
  20. Web前端期末大作业-粉色浪漫钻戒商城网站设计(HTML+CSS+JS)

热门文章

  1. 计算机网络学习笔记(12. 计算机网络体系结构概述)
  2. python通过解释器内置的open_Python 之 文件读写的学习
  3. 买彩票中奖的概率是多少?
  4. 想赚钱?找自己的长板!
  5. 查理.芒格,谈投资的秘密
  6. 打工好,还是创业好?
  7. 垄断者 Google Chrome 若被迫分拆,谁将会接盘?
  8. The authors of these two monitoring tools
  9. In this year of Hors, he is an adopted son
  10. Truffle测试框架