赞
踩
Lambda表达式是Java8中非常重要的一个新特性,其基于函数式编程的思想,支持将代码作为方法参数进行使用。可以
把Lambda表达式理解为通过一种更加简洁的方式表示可传递的匿名函数。
它本身没有名称,而且不像方法那样属于某一个类,但是可以有参数列表、代码体、返回值。使用了Lambda表达式之后就不需要再去编写匿名类了。
(参数列表) -> {
方法体
}
参数列表:即匿名方法的形参
-> :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;
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+","));
}
//匿名内部类
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Hello world !");
}
};
//使用Lambda
Runnable runnable1 = ()-> System.out.println("hello world");
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))); }
定义一个使用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(); } }
将当前.java文件编译生成.class文件,执行命令后,会在当前文件夹生成对应的.class文件
javac SourceDemo.java
将.class文件进行反编译,查看文件内容:
javap -p SourceDemo.class
生成内容如下:
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);
}
此时可以发现,代码中执行Lambda表达式的部分生成了一个静态私有函数
。这个静态私有函数的函数干就是Lambda表达式里面的内容。
那么对于这个静态私有函数,在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(); }
如果想查看内部类里面的内容,可以在lambda表达式执行之前,添加:
System.setProperty("jdk.internal.lambda.dumpProxyClasses", "D://");
这个方法会将运行时生成的内部类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
}
此时可以发现编译后的Lambda表达式已经被执行。
综上所述,Lambda表达式在执行的时候,会调用LambdaMetafactory.metafactory动态的生成内部类,在方法内调用SourceDemo$&Lambda$1,内部类里的调用方法块并不是动态生成的,只是在原class里已经编译生成了一个静态的方法,内部类只需要调用该静态方法。
我们点进去这个实例,查看forEach的源码:
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
我们发现,传递的参数,就是一个Consumer。
而Consumer就是JDK8自带的Function函数:
函数式接口-lambda函数与jdk8自带的函数接口
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。