当前位置:   article > 正文

JDK8:Lambda表达式使用介绍,Lambda表达式源码及原理分析_jdk8 lamda表达式用法

jdk8 lamda表达式用法

一、Lambda表达式使用

1、Lambda表达式介绍

Lambda表达式是Java8中非常重要的一个新特性,其基于函数式编程的思想,支持将代码作为方法参数进行使用。可以
把Lambda表达式理解为通过一种更加简洁的方式表示可传递的匿名函数。

它本身没有名称,而且不像方法那样属于某一个类,但是可以有参数列表、代码体、返回值。使用了Lambda表达式之后就不需要再去编写匿名类了。

2、Lambda使用规范

(1)Lambda基础格式

(参数列表) -> {
	方法体
}
  • 1
  • 2
  • 3

参数列表:即匿名方法的形参
-> :Lambda运算符
方法体:用于执行业务逻辑。可以是单一语句,也可以是语句块。如果是单一语句,可以省略花括号。当需要返回值,如果方法体中只有一条语句,可以省略return,会自动根据结果进行返回。

// 1)没有参数的Lambda表达式
()->new Student();

// 2)只有一个参数的Lambda表达式
x -> {
	System.out.println(x);
	return x;
}

// 3)有多个参数的Lambda表达式
(int x,int y) ->{
	System.out.println(x);
	System.out.println(x);
	return x+y;
}
// 上述可以进行简写,因为在Lambda中,参数列表中参数的数据类型可以交给JVM根据上下文进行推断。所以可以不用定义类型。
(x,y) ->{
	System.out.println(x);
	System.out.println(y);
	return x+y;
}

// 4)一个参数和仅一条语句的Lambda表达式
x -> 3+x;

// 5)多个参数和仅一条语句的Lambda表达式
(x,y) -> x+y;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

3、Lambda表达式与传统方式比对

(1)遍历集合

public static void main(String[] args) {
	String[] language = {"c", "c++",
							"c#",
							"java","python",
							"go","hive",
							"php"};
	List<String> languageList = Arrays.asList(language);
	//旧的循环方式
	for (String s : languageList) {
		System.out.println(s+",");
	}
	//lambda循环
	languageList.forEach(s-> System.out.println(s+","));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

(2)使用Lambda替换匿名内部类使用

//匿名内部类
Runnable runnable = new Runnable() {
	@Override
	public void run() {
		System.out.println("Hello world !");
	}
};

//使用Lambda
Runnable runnable1 = ()-> System.out.println("hello world");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

(3)实现Lambda实现集合排序

public static void main(String[] args) {

	String[] language = {"c", "c++",
						"c#",
						"java","python",
						"go","hive",
						"php"};
	
	//旧的循环比较方式
	Arrays.sort(language,new Comparator<String>(){
		@Override
		public int compare(String o1, String o2) {
			return (o1.compareTo(o2));
		}
	});
	
	//lambda循环比较
	Arrays.sort(language,(o1,o2)-> (o1.compareTo(o2)));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

二、Lambda表达式底层原理解析

1、反编译lambda

定义一个使用Lambda表达式的方法:

public class SourceDemo {

    public static void demo(){

        String[] language = {"c", "c++",
                "c#",
                "java","python",
                "go","hive",
                "php"};

        List<String> list = Arrays.asList(language);
        list.forEach(s-> System.out.println(s));
    }

    public static void main(String[] args) {
        SourceDemo.demo();
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

将当前.java文件编译生成.class文件,执行命令后,会在当前文件夹生成对应的.class文件

javac SourceDemo.java
  • 1

将.class文件进行反编译,查看文件内容:

javap -p SourceDemo.class
  • 1

生成内容如下:

Compiled from "SourceDemo.java"
public class com.itheima.lambda.SourceDemo {
  public com.itheima.lambda.SourceDemo();
  public static void demo();
  public static void main(java.lang.String[]);
  private static void lambda$demo$0(java.lang.String);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

此时可以发现,代码中执行Lambda表达式的部分生成了一个静态私有函数。这个静态私有函数的函数干就是Lambda表达式里面的内容。

2、静态私有函数生成过程

那么对于这个静态私有函数,在JDK8内部是如何实现调用的呢?可以查看LambdaMetafactory类,该类下有一个metafactory方法,lambda表达式每一次在执行的时候都会进入到这个方法中,并且为lambda表达式创建一个内部类。

// java.lang.invoke.LambdaMetafactory#metafactory
public static CallSite metafactory(MethodHandles.Lookup caller,
                                   String invokedName,
                                   MethodType invokedType,
                                   MethodType samMethodType,
                                   MethodHandle implMethod,
                                   MethodType instantiatedMethodType)
        throws LambdaConversionException {
    AbstractValidatingLambdaMetafactory mf;
    mf = new InnerClassLambdaMetafactory(caller, invokedType,
                                         invokedName, samMethodType,
                                         implMethod, instantiatedMethodType,
                                         false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);
    mf.validateMetafactoryArgs();
    return mf.buildCallSite();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

(1)查看内部类的内容

如果想查看内部类里面的内容,可以在lambda表达式执行之前,添加:

System.setProperty("jdk.internal.lambda.dumpProxyClasses", "D://");
  • 1

这个方法会将运行时生成的内部类class文件进行输出到D盘。
当该文件生成后,可以通过javap -c -p class文件名查看文件中的内容:

final class com.itheima.lambda.SourceDemo$$Lambda$1 implements java.util.function.Consumer {
  private com.itheima.lambda.SourceDemo$$Lambda$1();
    Code:
       0: aload_0
       1: invokespecial #10                 // Method java/lang/Object."<init>":()V
       4: return

  public void accept(java.lang.Object);
    Code:
       0: aload_1
       1: checkcast     #14                 // class java/lang/String
       4: invokestatic  #20                 // Method com/itheima/lambda/SourceDemo.lambda$demo$53:(Ljava/lang/String;)V
       7: return
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

此时可以发现编译后的Lambda表达式已经被执行。

综上所述,Lambda表达式在执行的时候,会调用LambdaMetafactory.metafactory动态的生成内部类,在方法内调用SourceDemo$&Lambda$1,内部类里的调用方法块并不是动态生成的,只是在原class里已经编译生成了一个静态的方法,内部类只需要调用该静态方法。

3、forEach分析

我们点进去这个实例,查看forEach的源码:

default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

我们发现,传递的参数,就是一个Consumer。

而Consumer就是JDK8自带的Function函数:
函数式接口-lambda函数与jdk8自带的函数接口

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/726796
推荐阅读
相关标签
  

闽ICP备14008679号