JAVA8 Lambda 不再害怕
本文就是要搞透彻 java8的Lambda ,后面自己code代码时尽量要用。否则以后看新的框架代码都很难看懂。
函数式接口
接口中 有且 只有一个抽象方法叫做函数式接口。@FunctionalInterface 修饰一个函数式接口,编译器会检测 @FunctionalInterface 修饰的接口,如果不是函数式接口就会报错。
@FunctionalInterface
interface A {
public void a();
}
如果是下面
Invalid '@FunctionalInterface' annotation; Lambada.A is not a functional interface
注意,当你不用 FunctionalInterface 修饰时,接口并不代表就不是函数式接口了,@FunctionalInterface 只是一个规范接口而已。
Lambda
延迟加载功能
下面这段代码, 那个a.split 方法简直就是白执行了。
private static void log(int a , String[] str){
if(a % 2 == 0){
System.out.println(str);
}
}
public static void main(String[] args) {
String a = "a,b,c,d";
log(1, a.split(","));
}
如果换成Lambda 表达式:
@FunctionalInterface
interface Call {
public Object call();
}
private static void log(int a, Call call) {
if (a % 2 == 0) {
System.out.println(call.call());
}
}
public static void main(String[] args) {
String a = "a,b,c,d";
log(2, () -> a.split(","));
}
当我们传入2时,call被回调,lambda表达式被执行,split才会被有效执行。lambda表达式 必须是函数式接口。
lambda 与函数式接口对应关系
----------------------无参数,无返回值-----------
@FunctionalInterface
public A {
public void call();
}
()->{
;; // 代码块
}
----------------------有参数,无返回值-----------
@FunctionalInterface
public A {
public void call(String a , String b);
}
(a,b)-{}
----------------------有参数,有返回值,无代码块-----------
@FunctionalInterface
public A {
public String call(String a);
}
// 一个参数 参数可以省略() , 没有代码块,返回值retuen可省略
a->{"我是返回值"}
----------------------有参数,有返回值,有代码块-----------
@FunctionalInterface
public A {
public String call(String a);
}
a->{
; ; // 代码块
return "我是返回值"
}
Java8内置的几个重要的函数接口
Predicate 代表条件, 用法:
// 定义一个条件 : 是否是女士 (字符串里面有女这个词)
Predicate<String> pre = name -> name.contains("女"); // 定义一个lamdba接口
// 造人
List<String> persons = Arrays.asList("小明-男", "花花-女", "郑明桥-男", "姑姑-男");
// 赛选出 女士
List<String> womans = persons.stream().filter(pre).collect(Collectors.toList());
System.out.println(womans); // [花花-女]
集合的stream + filter 方法 可以 处理集合,传入一个Predicate ,最后 collect 就会把集合中元素 包含 "女" 的元素 返回出来。
Predicate.negate() 取反
// 定义一个条件 : 是否是女士 (字符串里面有女这个词)
Predicate<String> pre = name -> name.contains("女"); // 定义一个lamdba接口
// 造人
List<String> persons = Arrays.asList("小明-男", "花花-女", "郑明桥-男", "姑姑-男");
// 取反 ,赛选出男生
List<String> mans = persons.stream().filter(pre.negate()).collect(Collectors.toList());
System.out.println(mans); // [小明-男, 郑明桥-男, 姑姑-男]
Predicate.or() 取并集
// 定义一个条件 : 是否是女士 (字符串里面有女这个词)
Predicate<String> pre = name -> name.contains("女"); // 定义一个lamdba接口
// 造人
List<String> persons = Arrays.asList("小明-男", "花花-女", "郑明桥-男", "姑姑-男");
// 取反 ,赛选出男生
List<String> mans = persons.stream().filter(pre.or(pre.negate())).collect(Collectors.toList());
System.out.println(mans); // [小明-男, 郑明桥-男, 姑姑-男]
pre.or(pre.negate()) : 女生条件 or 男生条件 , 取所有。
Consumer 代表消费, 用法:
List<String> persons = Arrays.asList("小明-男", "花花-女", "郑明桥-男", "姑姑-男");
// 遍历输出
persons.forEach(name -> System.out.println(name));
//遍历输出 consumer 作为 方法引用
persons.forEach(System.out::println);
forEach 参数就是一个consumer接口,直接消费拿出集合的每个元素。
Function 代表功能 , 用法:
List<String> persons = Arrays.asList("小明-男", "花花-女", "郑明桥-男", "姑姑-男");
List<Integer> lens = persons.stream().map(name -> name.length()).collect(Collectors.toList());
lens.forEach(System.out::println); // 4 , 4 ,5 ,4
map 参数 接受一个Function , 我们把原始的List<String> 数据结构 转换成了 List<Integer> 。 Function 接口是一个功能型接口,它的一个作用就是转换作用,将输入数据转换成另一种形式的输出数据。
lambda 原理
比如有如下代码:
public class Lambada {
@FunctionalInterface
interface Call {
public Object call();
}
private static void log(int a, Call call) {
if (a % 2 == 0) {
System.out.println(call.call());
}
}
public static void main(String[] args) {
log(12, () -> "我是日志内容");
}
}
我们用 javap -p Lambada.class 反编译出里面的方法,发现:
E:\debug_jdk\debug_jdk8\bin\debug_jdk8>javap -p Lambada.class
Compiled from "Lambada.java"
public class debug_jdk8.Lambada {
public debug_jdk8.Lambada();
private static void log(int, debug_jdk8.Lambada$Call);
public static void main(java.lang.String[]);
private static java.lang.Object lambda$0();
}
多了一个 lambda$0 方法,Lambada其实很简单,编译成class的时候,多加了个静态的 lambda$0 方法。运行遇到lambada时,就会直接调用 lambda$0 方法。
我们搞个事情:
public class Lambada {
@FunctionalInterface
interface Call {
public Object call();
}
private static void log(int a, Call call) {
if (a % 2 == 0) {
System.out.println(call.call());
}
}
public static void main(String[] args) {
log(12, () -> "我是日志内容");
}
private static java.lang.Object lambda$0() {
return "我是日志内容";
}
}
直接在代码中定义了 lambda$0 方法,运行自然会报错:
Exception in thread "main" java.lang.ClassFormatError: Duplicate method name "lambda$0" with signature "()Ljava.lang.Object;" in class file debug_jdk8/Lambada
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)