概述
Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。
冗余的Runnable代码
我们在上一篇文章中讲到多线程时,常常用到Runnable接口定义线程的任务
public class demo03InnerThread {
public static void main(String[] args) {
Runnable r = new Runnable(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
new Thread(r).start();
}
}
在匿名内部类中,其实只有方法体才是关键所在,那我们可以不创建这个匿名内部类吗?
Lambda初体验
public class demo01简化Runnable {
public static void main(String[] args) {
new Thread(() -> System.out.println(Thread.currentThread().getName())).start();
}
}
这样我们就将代码简化了
- ()表示run()方法的参数,此处没有参数‘
- ->表示将前面的参数传递给后面的代码
- 后面的输出语句即为业务逻辑代码
Lambda表达式的标准格式
Lambda表达式省去了面向对象的一些条条框框,格式由3部分组成:
- 一些参数
- 一个箭头
- 一段代码
(参数类型 参数名称) -> {代码}
没有参数括号中就没有内容,多个参数用逗号隔开
再来举个例子,在排序的时候我们经常用Arrays.sort()方法传入一个比较器自定义比较规则,我么们也可以用Lambda表达式简写
import java.util.Arrays;
import java.util.Comparator;
public class demo02简化Comparator {
public static void main(String[] args) {
Integer[] arr = {98, -4, 2, 7, 11, 4, 8};
//降序排序
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
//Lambda表达式
//Arrays.sort(arr, (Integer o1, Integer o2) -> o2 - o1);
Arrays.sort(arr, (o1, o2) -> o2 - o1);//参数类型可以不写
}
}
可以省略的内容:
- 参数列表的数据类型可以省略不写
- 如果参数只有一个,括号也可以不写
- 代码只有一行,{}, return 和分号 可以不写(要不写则都不写)
Lambda使用前提
-
必须具有接口,且接口中仅有一个抽象方法
-
必须具有上下文推断(参数类型需和接口对应)
备注:只有一个抽象方法的接口成为函数式接口
函数式接口
概述
函数式接口在Java中指的是有且仅有一个抽象方法的接口
函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda。
备注:
“语法糖”是指使用更方便,但原理不变的代码语法。
例如遍历集合时使用的for-each循环,其实底层的实现原理仍然是迭代器,这便是“语法糖”
从应用层面上来讲,Java中的Lambda可以被当作是匿名内部类的“语法糖”,但是二者在原理上不同
注:我们可以用@FunctionInterface
注解检测接口是否是一个函数式接口
函数式编程
有些场景的代码在执行后,结果不一定被使用,从而造成浪费。而Lambda表达式是延迟执行的,正好可以作为解决方案,提升性能。
** 性能浪费的日志案例**
package Lambda;
/*
发现以下代码存在性能浪费的问题:
调用ShowLog方法,传递的第二个参数是一个拼接后的字符串
先把字符串拼接好,再调用方法
如果传递的leval等级不为1,则不会输出这个字符串,那么拼接的这个字符串就浪费了
*/
public class demo03性能浪费的日志案例 {
//根据日志的级别显示日志信息
public static void showLog(int level, String message){
if(level == 1)
System.out.println(message);
}
public static void main(String[] args) {
String message1 = "hello";
String message2 = "hi";
String message3 = "happy";
String message1 = "hello";
String message2 = "hi";
String message3 = "happy";
showLog(2, message1 + message2 + message3);
}
}
日志案例的优化
package Lambda;
/*
使用Lambda优化日志案例
Lambda的特点:延迟加载
Lambda使用的前提:存在函数式接口
这里使用Lambda表达式,作为参数传递给showLog方法,
仅当level为1时才会调用接口中的方法,才会进行字符串的拼接
如果level不为1,不会进行字符串的拼接9=
*/
public class demo04日志案例的优化 {
@FunctionalInterface
public interface MessageBuilder{
//定义一个拼接消息的抽象方法
public abstract String builder();
}
public static void showLog(int level, MessageBuilder mb){
if(level == 1)
System.out.println(mb.builder());
}
public static void main(String[] args) {
String message1 = "hello ";
String message2 = "hi ";
String message3 = "happy";
showLog(1, () -> message1 + message2 + message3);
}
}
常用的函数式接口
Supplier接口
java.util.function.Supplier<T>
接口仅包含一个无参的方法<T> get()
。用来获取一个泛型参数指定类型的对象数据。
package Lambda;
import java.util.function.Supplier;
/*
Supplier接口被称为生产型接口,指定接口的泛型是什么类型,接口的中的get方法就会生产什么类型的数据
*/
public class demo05Supplier {
public static String getString(Supplier<String> sup){
return sup.get();
}
public static void main(String[] args) {
System.out.println(getString(() -> "我是彭于晏"));
}
}
package Lambda;
import java.util.function.Supplier;
/*
Supplier接口被称为生产型接口,指定接口的泛型是什么类型,接口的中的get方法就会生产什么类型的数据
*/
public class demo06Supplier求数组最大元素 {
public static Integer getMax(Supplier<Integer> sup){
return sup.get();
}
public static void main(String[] args) {
int[] nums = {3,5,2,7,1,9,8,23};
System.out.println(getMax(() -> {
int max = nums[0];
for (int num : nums)
max = Math.max(max, num);
return max;
}));
}
}
Consumer接口
java.util.function.Consumerr<T>
接口与Supplier接口正好相反,他不是生产一个数据,而是消费一个数据,数据类型由泛型决定。
package Lambda;
import java.util.function.Consumer;
public class demo07Consumer {
public static void solution(String name, Consumer<String> con){
con.accept(name);
}
public static void main(String[] args) {
//消费数据的方式由自己定义
solution("彭于晏", (name) -> System.out.println("我是" + name));
solution("彭于晏", (name) -> System.out.println(new StringBuilder(name).reverse().toString()));
}
}