场景
-
我们在使用
Java
的Comparator
进行排序, 或Runnable
进行运行线程时总是需要new
一个新的匿名类出来, 并且需要实现抽象方法,有么有更方便的做法呢? -
Java SE 8
开始已经可以支持Lambda
表达式,它如何使用呢?
说明
- 在
Lambda
表达式出现以前, 我们实现一个Comparator
比较器需要new
一个匿名类, 并重写compare
方法, 编译器编译时还是生成这个匿名内部类的.class
文件. 这种匿名内部类借助IDE
生成时并不麻烦, 但是如果进行函数流式调用,a.x(new Comparator(){...}).y(new Comparator(){...})
这样的话看起来就不直观了. 除此以外生成大量的.class
文件总是看起来很冗余.
ls.sort(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
Lambda
出现以后对于内部类的使用是一种补充,在函数式编程时,比如上边说的流式调用很方便直观,因为我们传入的lambda
表达式是一个匿名函数, 更直观易懂,代码量少,减少大括号,圆括号的视觉干扰.
ls.sort((a,b)->b.compareTo(a));
-
Lambda
调用不需要生成匿名内部类, 调用更快,性能更高. 因为JDK8实现Lambda
调用方式是通过Invokedynamic
指令的. -
它本质上就是一个匿名函数,遵守函数的声明规则. 即参数,函数体,返回值.
-
Lambda
表达式也是实现了一个函数接口的唯一抽象方法,所以它一定对应着某个函数接口@FunctionalInterface
的唯一抽象方法.
@FunctionalInterface
interface Visitor<T>{
T visitor(T from);
}
-
Lambda
表达式方法体内引用的this
是外部方法的实例对象,Lambda
表达式并不是匿名内部类,只是一个函数,并没有自己的this
. -
Lambda
实现的的函数接口必须是具有唯一抽象方法的接口,我们可以用@FunctionalInterface
注解来限制这个接口,提供编译时检查。当然如果有接口只有唯一抽象方法,不声明@FunctionalInterface
也行.
语法
- 例子: 以下是
Comparator
接口的方法, 我们看看Lambda
表达式有几种写法.
public int compare(String a, String b){
return a.compareTo(b);
}
方式1
- 参数类型可省略,会根据方法原型自动推导为
String
,单语句时可以省略大括号和return
;以下b.compareTo(a)
编译器会根据compare
抽象方法原型自动推导是否需要返回.
(a,b)->b.compareTo(a)
方式2
- 有参数类型, 有大括号, 有
return
; 注意有大括号是方法原型如果需要返回,那么return
不能省略.
(String a,String b)->{
return b.compareTo(a);
}
方式3
- 方法引用是紧凑的
lambda
表达式, 直接用String的compareTo方法.
String::compareTo
例子
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@FunctionalInterface
interface Visitor<T>{
T visitor(T from);
}
// 不需要注释 @FunctionalInterface
// 但是如果用于 lambda实现,编译器默认它为 @FunctionalInterface
// 一个接口如果声明了多个抽象方法,在应用 lambda 表达式时会报错.
interface Book{
String query(String name);
}
// 不允许抽象类作为lambda表达式的函数接口,在应用 lambda表达式时编译报错.
abstract class Test1
{
abstract String query(String name);
}
public class TestLambda<T>{
private String name = "TestLambda";
public static void PRINT(String str){
System.out.println(str);
}
public static <T> void PRINTC(Collection<T> ls){
// Java8允许你通过::关键字获取方法或者构造函数的的引用
ls.forEach(System.out::println);
}
public void testLambda3(){
PRINT("================== testLambda3 ===================");
Book b = (s)->{
// this 引用的是当前方法的实例对象.
// 而匿名类引用的是匿名类的实例.
return s+"->"+this.name;
};
String s = b.query("Tobey");
PRINT(s);
}
public static void testLambda2() {
// 1. 未声明 @FunctionalInterface 的具有单个抽象方法的接口也能使用.
PRINT("================== testLambda2 ===================");
Book b = (s)->(s+"->");
String s = b.query("Tobey");
PRINT(s);
PRINT("=====================================");
// The target type of this expression must be a functional interface
// Test1 t1 = (s)->(s+"->");
// 2. Stream 类使用.
List<String> ls = Arrays.asList("123","hello","Tobey","go home");
Stream<String> ss = ls.stream();
List<String> ls1 = ss.filter(s1->s1.matches("^\\d+$")).collect(Collectors.toList());
PRINTC(ls1);
}
public static void testLambda(){
PRINT("================== testLambda ===================");
List<String> ls = Arrays.asList("csdn","infoworld","1","db","c2","2");
PRINTC(ls);
// 传统使用匿名类来创建比较器.
PRINT("================= Old Comparator ====================");
ls.sort(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
PRINTC(ls);
// 2. 使用lambda表达式来实现比较器,不带参数类型,因为编译器会自动推导.
PRINT("================= Use Lambda ====================");
ls.sort((a,b)->b.compareTo(a));
PRINTC(ls);
// 2.1 参数带类型, 使用{语句;}
PRINT("=====================================");
ls.sort((String a, String b)->{return a.compareTo(b);});
PRINTC(ls);
// 2.2 lambda表达式实现了Runnable的run()方法.
// -- 注意 Runnable接口声明了@FunctionalInterface注解.
try{
Thread t = new Thread(()->{
PRINT("Lambda instead of anonymous Runnable");
});
t.start();
t.join();
}catch(Exception e){}
// 3. lambda访问外部的final变量, 如果变量在上下文未被修改,会自动声明为final.
// -- 额外条件如果长度是2的,排在前面.
PRINT("=====================================");
int number = 2;
ls.sort((String a, String b)->{
// 编译报错:
// Local variable number defined in an enclosing scope must be final or effectively final
// number = 4;
if(a.length() == number)
return -1;
if(b.length() == number)
return 1;
return a.compareTo(b);
});
PRINTC(ls);
// 4. 声明注解为 @FunctionalInterface的接口,使用lambda表达式可以绑定默认的抽象方法.
// -- 注意Comparator接口声明@FunctionalInterface注解.
// -- 使用这种方式可以定义自己的方法参数支持lambda表达式.
// -- 如果只有一个参数,可把()都省略.
PRINT("=====================================");
Visitor<String> vs = a->{
PRINT(a);
return a+"-"+String.valueOf(a.hashCode());
};
String value = vs.visitor("infoworld");
PRINT(value);
}
public static void main(String[] args) {
System.out.println("Hello Tobey!");
testLambda();
testLambda2();
new TestLambda<>().testLambda3();
}
}
输出
Hello Tobey!
================== testLambda ===================
csdn
infoworld
1
db
c2
2
================= Old Comparator ====================
1
2
c2
csdn
db
infoworld
================= Use Lambda ====================
infoworld
db
csdn
c2
2
1
=====================================
1
2
c2
csdn
db
infoworld
Lambda instead of anonymous Runnable
=====================================
db
c2
1
2
csdn
infoworld
=====================================
infoworld
infoworld-1253667748
================== testLambda2 ===================
Tobey->
=====================================
123
================== testLambda3 ===================
Tobey->TestLambda
最后, 想查看Java8新特性和增强, 请移步 学院 JDK8-Java8-JavaSE8新特性和增强功能