你们好,小乐又来给你们分享Java8核心特性了,上一篇文章是《乐字节|Java8核心实战-接口默认方法》,此次就来说Java8核心特征之Lambda表达式。java
Java8 引入Lambda表达式,容许开发者将函数当成参数传递给某个方法,或者把代码自己看成数据进行处理。使用Lambda表达式,使得应用变得简洁而紧凑。 不少语言(Groovy、Scala等)从设计之初就支持Lambda表达式。可是java中使用的是 匿名内部类代替。最后借助强大的社区力量,找了一个折中的Lambda实现方案,能够实现简洁而紧凑的语言结构。数据库
匿名内部类,即一个没有名字的,存在于一个类或方法内部的类。当咱们须要用某个类且只须要用一次,建立和使用和二为一时,咱们能够选择匿名内部类,省掉咱们定义类的步骤。express
匿名内部类会隐士的继承一个类或实现一个接口,或者说匿名内部类是一个继承了该类或者实现了该接口的子类匿名对象。下面看一个匿名内部类的例子编程
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; }dom
Lambda 表达式示例:ide
使用Lambda时,实现方法能够有参数,也能够有返回值,若是没指定参数类型,则由编译器自行推断得出。函数式编程
生成[1,10]之间的任意整数函数
interface Model2{ int func(); } Model2 md2 = () -> {return (int)(Math.random()*10+1)};
Lambda的改写须要有对应的抽象方法,当没有参数时须要使用
()占位,当表达式只有一行代码时,能够省略return和{}
以上的Lambda等价于:
Model2 md2 = () -> (int)(Math.random()*10+1);
返回一个对数字描述的字符串。
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一并省去。
根据输入的运算符计算两个数的运算,并返回结果
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表达式,其中省略掉了每个参数的类型,编译器自动推断。多条语句时实现体的{}
不能省。
在Java8以前,接口能够做为方法参数传入,执行时必须提供接口实现类的实例。从java8开始,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做为参数 testLambda(()->{ System.out.println("能够简单,能够复杂"); }); // 带参Lambda做为参数 testLambdaParam("hello",(a)->{ System.out.println(a); });
在Lambda中能够定义本身的局部变量,也可使用外层方法的局部变量,还可使用属性。这一点也不难理解,既然是一个方法的实现,只写了一个代码块,那么使用自己所属方法的局部变量和类的属性也并不过度。
public static void main(String[] args) { List<String> strs = new ArrayList<String>(){ { add("aaa"); add("bbb"); add("ccc"); } }; int j = 1; strs.forEach((str)->{ int i = 0; System.out.println(str + " " + i + " " + j); }); }
排序对于久经开发的你来讲可能并不陌生,假如原来你作过电商项目,相信对于电商场景下的商品记录排序操做颇有感情,下面咱们使用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> goods= Arrays.asList(g01,g02,g03,g04,g05);
Collections.sort(goods,(g1,g2)->g1.getSale()-g2.getSale());
// 使用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());
对于项目开发日志打印是一项不可获取的模块,不管实在开发阶段仍是项目部署上线后,日志信息的输出对于开发人员来以及运维人员来讲都是一项重要的参考指标。
日志输出场景这里以用户模块UserService 为例,如下为优化前的日志输出代码:
public String login(String userName, String userPwd) { logger.info("UserService 接收到参数-->" + userName + "," + userPwd); /** * 登陆逻辑省略 */ return "login"; }
日志级别设置到debug,在开发阶段方便查看后端接收到的参数信息。仔细分析这里的日志代码,能够看到当日志级别设置为info 时 debug 日志不该该执行输出操做,同时这里调用debug 方法时,对于传入的字符串参数须要做对应的拼接操做,才会传入过来。当访问的状况在商城项目作活动状况下 这里的状况有可能会变得很糟糕:全部的debug 信息所有输出 同时会有大量字符串拼接操做,会影响整个应用程序的执行性能。
日志输出场景这里以用户模块UserService 为例,日志输出代码优化
/** * 添加info方法 * 判断日志打印级别 * 当条件成立时 输出日志信息 * @param logger * @param message */ public void info(Log logger, Supplier<String> 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的应用场景,从代码结构来讲一般是结合函数式接口来使用,使得开发是面向函数来进行编程,也是Java8引入的一种新的思想-函数式编程(后续会介绍)。同时还会结合前面讲到的接口默认方法提现到应用开发中。
乐字节原创文章,转载请注明出处和做者!
更多乐字节技术资料教程请来: 乐字节 腾讯课堂