Java 8新特性:深入理解Lambda表达式

1、Lambda表达式的使用

1.1、概念

Lambda 表达式的本质 : 函数式接口的对象,当一个函数式接口采用匿名内部类创建对象的时候,可以使用Lambda表达式

1.2、什么是函数接口?

函数式接口: 只有一个抽象方法的接口

//这个接口的作用是验证此为函数式接口,换句话说只要接口中只有一个抽象方法就是函数式接口,与加不加@FunctionalInterface没有关系
@FunctionalInterface
public interface MyInterface {
    void sout(); //只能有一个抽象方法,有两个抽象方法就会报错,主要是为了避免产生歧义,有多个方法就不知道调哪个了
}

Java中提供了4大基本的函数式接口:

    public interface Consumer<T> {
        void accept(T t) //消费型接口,接收一个参数对象但是不返回
    }  
    public interface Supplier<T>{
        T get() //供给型接口,不接受参数但是返回一个对象
    }  
    public interface Function<T,R>{
        R apply(T t) //函数型接口 ,接收T类型的参数,进行操作,返回R类型的对象
    } 
    public interface Predicate<T>{
        boolean test(T t)//断定型接口,接收T类型的参数,判断其是否满足某种约束,返回一个boolean值
    }  

代码举例:

    public static void main(String[] args) {
        List<String> list = Arrays.asList("北京" ,"南京","上海","武汉","广州","深圳");
        //未使用Lambda表达式时,才有匿名内部类创建函数式接口对象
        list.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
        //使用了Lambda表达式创建了Consumer这种函数式接口的对象,传入到方法中
        list.forEach(s -> {
            System.out.println(s);
        });
    }

其中foreach方法接收的是Consumer这种类型的函数型接口:

	default void forEach(Consumer<? super T> var1)

语法中可省略内容:

  • 参数列表:括号中的参数列表的数据类型,可以省略不写
  • 参数列表:括号中的参数如果只有一个,那么类型和()都可以省略
  • 一些代码:如果{}中的代码只有一行,无论是否有返回值,都可以省略{}return;

2、Lambda表达式的原理

2.1、思路总结

  • Lambda表达式是一个语法糖,在编译器会解语法糖,会生成invokedynamic字节码指令和一个私有的私有的Lambda方法
  • invokedynamic会调用BootstrapMethods引导方法,即调用LambdaMetafactory.metafactory(),这个方法会通过ASM字节码技术,在运行时动态的写入一个匿名内部类字节码,该类实现了函数式接口(这里是Consumer)
  • 通过反射创建这个匿名内部类对象,通过该对象去调用编译期生成的Lambda方法

归根结底:Lambda的一系列操作就是生成了一个函数式接口的实现,通过这个实现去调用对应的Lambda方法

语法糖:指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。简而言之,语法糖让程序更加简洁,有更高的可读性。
解语法糖:前面提到过,语法糖的存在主要是方便开发人员使用。但其实,Java虚拟机并不支持这些语法糖。是Java编译器实现的,这些语法糖在编译阶段就会被还原成简单的基础语法结构,这个过程就是解语法糖。

2.2、源码分析

源代码如下:

public class LambdaTest {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("C" , "h" , "a" ,"i" ,"m","e","k");
        list.forEach(s -> {
            System.out.print(s);
        });
    }
}

反编译如下:

public class LambdaTest {
    public static void main(String[] stringArray) {
        List<String> list = Arrays.asList("C", "h", "a", "i", "m", "e", "k");
  		//invokedynamic 指令会调用BootstrapMethods引导方法,即调用LambdaMetafactory.metafactory()
        list.forEach((Consumer<String>)LambdaMetafactory.metafactory(
            null, null, null,
            (Ljava/lang/Object;)V, 
            lambda$main$0(java.lang.String ),
            (Ljava/lang/String;)V)()
         );
    }
    private static /* synthetic */ void lambda$main$0(String string) {
        System.out.print(string);
    }
}

使用jclasslib查看:
在这里插入图片描述

invokedynamic会调用BootstrapMethods引导方法,即调用LambdaMetafactory.metafactory():

在这里插入图片描述

ASM字节码技术,在运行时动态的写入一个匿名内部类字节码,该类实现了函数式接口(这里是Consumer),此时并没有创建该对象

在这里插入图片描述

其中匿名内部类的类名为:

在这里插入图片描述

父接口为Consumer ,因为遍历使用的foreach要传入Consumer类型:
在这里插入图片描述

通过反射创建对象:

在这里插入图片描述
在这里插入图片描述

通过创建的匿名内部类对象调用Lambda表达式方法:

在这里插入图片描述 在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值