lambda表达式实战
从例子引出lambda
传递Runnable创建Thread
- java8之前
1 | Thread thread=new Thread(new Runnable() { |
- java 8 之后
1 | new Thread(()->{}); |
上边的例子比较简单,但是有两个疑问。什么是Lambda表达式?怎么使用lambda表达式?
什么是Lambda表达式?
从上述例子入手,首先我们知道Lambda一般代表的是一个匿名对象;其次我们点击“->”,IDE会帮助我们进入到符合Lambda规范的函数接口。我们来观察下这个符合规范的类的变化。
- java7 Runnable
1 | // 省略注释 |
- java8 Runnable
1 | // 省略注释 |
我们发现java8后Runnable接口新增了一个注解@FunctionalInterface。下边我们一起来看下这个注解是什么。
FunctionalInterface
1 | /** |
- 上边文档的核心意思是:@FunctionInterface注解是为了表明这个类是一个函数式接口。
- 函数式接口有这样的特点:只有一个抽象方法。java8提供了default方法,以及超类Object中的方法(toString,Equals),这些方法不计算抽象方法数量的统计中。
- 使用上:函数式接口可以配合lambda表达式、方法引用、构造引用使用。
- 如果类上标记了这个注解,编译器会在编译期进行检查
- 最后,即使我们没有标注这个注解,编译器也会将它看待成一个函数式接口
好了,从上边我们知道了lambda的特点,接下来我们来聊下怎么使用?
如何使用Lambda
首先,我们去官网查阅Java8新特性,找到Lambda表达式的说明。我们从这个文档的“Syntax of Lambda Expressions”部分入手,大概可以得到如下的结论。
Lambda的组成
Lambda主要由下边几部分组成;参数列表,连接符,主体。
参数列表
- 圆括号内部,参数以“,”分割开来。如(String a,Object b)。
- 此外,参数的类型和括号,有些时候是可以省略
箭头记号
- 通过“->”这种特殊符号形式,连接前后。
主体
可以由单个表达式,或者语句块组成。
单个表达式,如”System.out.println(“xxx”)”
语句块
- 示例1
1
2
3{
System.out.println("xxx");
}- 示例2
1
2
3
4{
// do something return some result
return 100;
}
Lambda的完整用法示例
无返回值的lambda的用例
目的,将具体业务实现交给调用者处理。
- 定义一个无返回值,符合FunctionInterface规范的接口对象
1 | interface Print<String>{ |
使用示例1
我这里的业务逻辑是根据输入参数,执行日志打印操作。实际业务场景下,可能对应的是发送邮件或者MQ这样的具体操作。
1 | public class LambdaDemo { |
使用示例1 的延伸使用
- 定义 一个使用类
1 | class Doctor{ |
- 具体使用
1 | Doctor doctor=new Doctor("baigt","java and javascript"); |
有返回值的lambda的用例
目的,将具体业务实现交给调用者处理,并将结果返回。
- 定义一个有返回值,符合FunctionInterface规范的接口对象
1 | interface GetSomething<String>{ |
- 定义一个使用者
1 | class Doctor{ |
- 使用示例
我这里的业务逻辑是根据输入参数(隐式interest),计算出一个结果返回出来,并对这个结果执行打印操作。
1 | Doctor doctor=new Doctor("baigt","java and javascript"); |
到此处,我们已经大概明白lambda表达式的基本用法。但是还会有两个疑问?
- 上边例子我们自定义了几个函数式接口,那么还有其他常用的函数式接口?
- 函数式接口不仅可以通过lambda表达式使用,还可以通过方法引用和构造引用来使用。那么这种引用又是怎么回事?
常用函数接口
我们选中@FunctionInterface注解类,通过Ide的Find Usages功能,会发现在java.util.function包下java8新增了很多类。这里挑几个基础的(其他的基本是功能上的增强或变种)来说。大致上有这么几种。
- Consumer
- Supplier
- Predicate
- Function
下边会做一个简单的说明和使用。可能不会细致的去讲每一个Api。旨在让大家快速熟悉使用java8 lambda。
Consumer
1 | /** |
首先此接口只有一个抽象方法accept,该方法接收一个入参,不返回结果。
定义使用类
1 | public static void doConsumer(Consumer consumer,String input) { |
- 使用示例1
接收 “something input”输入,并执行打印操作
1 | Consumer consumer = input -> System.out.println(input); |
- 使用示例2
将两个Consumer操作串连起来,andThen的后执行。
1 | Consumer consumer = input -> System.out.println(input); |
Supplier
1 | /** |
首先此接口只有一个抽象方法get,该方法不接收参数,返回一个T类型的结果。
定义使用类
1 | public static <T> T doSupplier(Supplier<T> supplier) { |
- 使用示例1
不传入参数,生成一个指定类型为String或Integer的对象
1 | System.out.println(doSupplier(() -> "baigt")); |
Predicate
1 | import java.util.Objects; |
首先此接口只有一个抽象方法test,该方法接受一个T类型的对象,返回一个boolean类型的结果。
定义使用类
1 | public static boolean doPredicate(Predicate<String> predicate,String string) { |
- 使用示例1
根据条件,判断输入对象是否符合过滤规则。
1 | System.out.println(doPredicate(input -> input.length() > 5, "12345")); |
Function
1 | import java.util.Objects; |
首先此接口只有一个抽象方法apply,该方法接收一个T类型对象,返回一个R类型的结果。
定义使用类
1 | public static Integer doFunction(Function<String,Integer> function,String input) { |
- 使用示例1
接收一个String类型的入参,返回Integer类型的结果。示例中没做具体异常判断。
1 | System.out.println(doFunction(input -> input.length(), "baigt")); |
compose是先执行的部分,上述例子中,是根据输入参数进行进一步的加工,再作为输入参数传递给具体调用者。
引用
前边提到了方法引用和构造引用两种,其实构造引用是一种特殊方法引用。具体参照官方文档说明中“Kinds of Method References”部分。
种类 | 用例 |
---|---|
类名::静态方法 | String::valueOf |
实例对象::实例方法 | doctor1::getInterest |
类名::实例方法 | String::toUpperCase |
类名::new (构造引用) | String::new |
静态引用
- 使用类
1 | public static String doStaticReference(Function<Integer,String> function, Integer input) { |
- 示例
1 | doStaticReference(String::valueOf,123456); |
实例对象引用实例方法
- 使用类
1 | class Doctor{ |
- 示例
1 | Doctor doctor1=new Doctor("baigt007","java"); |
类引用实例方法
- 使用类
1 | public static String doMethodReference(Function<String,String> function, String input) { |
- 示例
1 | doMethodReference(String::toUpperCase,"baigt");O |
构造引用
- 示例
1 | Supplier<String> stringInstance = String::new; |
原文地址:https://my.oschina.net/lt0314/blog/3144851
如果大家喜欢我的文章,可以关注个人订阅号。欢迎随时留言、交流。