JDK8新特性(一) --- Lambda表达式
Lambda表达式
Lambda是一个匿名函数,可以理解为一段可以传递的代码。
匿名内部类语法冗余,Lmabda是简化匿名内部类的简写。
Lambda的标准格式
Lambda省去面向对象的条条框框,Lambda的标准格式格式由3个部分组成:
(参数类型 参数名称) -> {
代码体;
}
//(参数类型,参数名称):方法的参数列表
//代码体:方法体
-> :箭头,分割参数列表和方法体
1、无参无返回值的Lambda
先定义一个接口
interface Swimmable {
public abstract void swimming();
}
创建匿名内部类
public class Demo02LambdaUse {
public static void main(String[] args) {
goSwimming(new Swimmable() {
@Override
public void swimming() {
System.out.println("匿名内部类游泳");
}
});
//Lambda表达式的实现方式
goSwimming(() -> {
System.out.println("Lambda游泳");
});
}
public static void goSwimming(Swimmable swimmable) {
swimmable.swimming();
}
}
2、有参有返回值的Lambda
java.util.Comparator
public abstract int compare(T o1, T o2);
当需要对一个对象集合进行排序时, Collections.sort 方法需要一个 Comparator
接口实例来指定排序的规则。
//定义需要排序的对象
public class Person {
private String name;
private int age;
private int height;
//省略
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
public class Demo03LambdaUse {
public static void main(String[] args) {
ArrayList<Person> persons = new ArrayList<>();
persons.add(new Person("AA", 58, 174));
persons.add(new Person("BB", 58, 176));
persons.add(new Person("CC", 54, 171));
persons.add(new Person("DD", 53, 178));
Collections.sort(persons,new Comparator<Person>){
@Override
public int compare(Person p1,Person p2){
return p1.getAge() - p2.getAge();
}
});
//lambda写法
Collection.sort(persons,(Person p1,Person p2) ->{
return p1.getAge() - p2.getAge();
})
}
}
3、对forEach()的简写
List<Integer> list = Arrays.asList(11, 22, 33, 44);
list.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
System.out.println("-----------------");
list.forEach((s) -> {
System.out.println(s);
});
总结:
以后我们调用方法时,看到参数是接口就可以考虑使用Lambda表达式,
Lambda表达式相当于是对接口中抽象方法的重写
Lambda的实现原理
\\实现接口
@FunctionalInterface
interface Swimmable {
public abstract void swimming();
}
使用匿名内部类实现
public class Demo04LambdaImpl {
public static void main(String[] args) {
goSwimming(new Swimmable() {
@Override
public void swimming() {
System.out.println("使用匿名内部类实现游泳");
}
});
}
public static void goSwimming(Swimmable swimmable) {
swimmable.swimming();
}
}
运行后可以看到匿名内部类会在编译后产生一个类: Demo04LambdaImpl$1.class
//反编译后可以看到如下代码
import java.io.PrintStream;
// Swimmable, Demo04LambdaImpl
static class Demo04LambdaImpl$1 implements Swimmable {
public void swimming()
{
System.out.println("使用匿名内部类实现游泳");
}
Demo04LambdaImpl$1() {
}
}
使用Lambda表达式
public class Demo04LambdaImpl {
public static void main(String[] args) {
goSwimming(() -> {
System.out.println("使用匿名内部类实现游泳");
});
}
public static void goSwimming(Swimmable swimmable) {
swimmable.swimming();
}
}
运行程序后并没有出现一个新的类,使用了Lambda后使用XJad反编译工具也无法反编译。
我们使用JDK自带的一个工具: javap ,对字节码进行反汇编,查看字节码指令。
\\可以在DOS中输入命令进行反编译
javap -c -p 文件名.class
-c:表示对代码进行反汇编
-p:显示所有类和成员
可以看到在类中多出了一个私有的静态方法 lambda$main$0 。对代码进行debug,
可以确认 lambda$main$0 里面放的就是Lambda中的内容.
我们可以这么理解 lambda$main$0 方法:
//关于这个方法 lambda$main$0 的命名:
//以lambda开头,因为是在main()函数里使用了lambda表达式,
//所以带有$main表示,因为是第一个,所以$0。
public class Demo04LambdaImpl {
public static void main(String[] args) {
...
}
private static void lambda$main$0() {
System.out.println("Lambda游泳");
}
}
//Lambda在运行的时候会生成一个内部类,调用该方法。
//为了验证是否生成内部类,可以在DOS界面运行java文件时加上 -Djdk.internal.lambda.dumpProxyClasses
//加上这个参数后,运行时会将生成的内部类class码输出到一个文件中。
//在DOS命令行输入以下命令
C:\Users\>java -Djdk.internal.lambda.dumpProxyClasses
com.lhc.demo01lambda.Demo04LambdaImpl
Lambda游泳
执行完毕生成了一个新的类:
反编译Demo04LambdaImpl$$Lambda$1.class字节码文件
//可以看到这个匿名内部类实现了 Swimmable 接口,并且重写了 swimming 方法,
//swimming方法调用Demo04LambdaImpl.lambda$main$0(),也就是调用Lambda中的内容。
// Referenced classes of package com.lhc.demo01lambda:
// Swimmable, Demo04LambdaImpl
final class Demo04LambdaImpl$$Lambda$1 implements Swimmable {
public void swimming()
{
Demo04LambdaImpl.lambda$main$0();
}
private Demo04LambdaImpl$$Lambda$1()
{
}
}
所以可以理解为:
public class Demo04LambdaImpl {
public static void main(String[] args) {
goSwimming(new Swimmable() {
public void swimming() {
Demo04LambdaImpl.lambda$main$0();
}
});
}
private static void lambda$main$0() {
System.out.println("Lambda表达式游泳");
}
public static void goSwimming(Swimmable swimmable) {
swimmable.swimming();
}
}
//Lambda在程序运行的时候形成一个类
//1. 在类中新增一个方法,这个方法的方法体就是Lambda表达式中的代码
//2. 还会形成一个匿名内部类,实现接口,重写抽象方法
//3. 在接口的重写方法中会调用新生成的方法.
Lambda省略格式
在Lambda标准格式的基础上,使用省略写法的规则为:
- 小括号内参数的类型可以省略
- 如果小括号内有且仅有一个参数,则小括号可以省略
- 如果大括号内有且仅有一个语句,可以同时省略大括号、return关键字及语句分号
//省略前
(int a) -> {
return new Person();
}
//省略后
a -> new Person();
Lambda的前提条件
使用Lambda需要满足两个条件:
- 方法的参数或局部变量类型必须为接口才能使用Lambda
- 接口中有且仅有一个抽象方法
否则使用的时候会报错
函数式接口
接口中有且仅有一个抽象方法的接口---函数时接口
在JDK1.8中引入了一个新的注解:@FunctionalInterface,可以标记在一个接口上表明是一个函数式接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。
Lambda和匿名内部类的对比
1. 所需的类型不一样
匿名内部类,需要的类型可以是类,抽象类,接口
Lambda表达式,需要的类型必须是接口
2. 抽象方法的数量不一样
匿名内部类所需的接口中抽象方法的数量随意
Lambda表达式所需的接口只能有一个抽象方法
3. 实现原理不同
匿名内部类是在编译后会形成class
Lambda表达式是在程序运行的时候动态生成class
本文作者:夜色哪里都是美
本文链接:https://www.cnblogs.com/evenIer/p/16411944.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步