好程式員分享java8新特性之Lambda表達式
⼀、Lambda表達式簡介
什麼是Lambda?
Lambda表達式是Java 8推出的⼀個新特性。從本質上講,Lambda表達式是⼀個匿名函數。
為什麼要使⽤Lambda?
使⽤Lambda表達式可以對⼀個接⼝進⾏⾮常簡潔的實作。
之前我們在給⼀個接⼝引⽤指派的時候,可以使⽤接⼝實作類,或者匿名内部類。但是有了
Lambda表達式,我們可以更加⽅便的實作這個需求。
interface Comparator {
int compare(T o1, T o2);
}
class Program {
public static void main(String[] args) {
// 1. 使⽤接⼝實作類實作
class Impl implements Comparator {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
Comparator c1 = new Impl();
// 2. 使⽤匿名内部類實作
Comparator c2 = new Comparator() {
};
// 3. 使⽤Lambda表達式實作
Comparator c3 = (o1, o2) -> o1 - o2;
從上述例⼦中,我們可以看到: 對同樣⼀個接⼝引⽤的實作,Lambda最簡單!
Lambda對接⼝的要求?雖然Lambda表達式可以很便捷的實作接⼝,但并不是所有的接⼝都可以使⽤Lambda表達式來實作。
可以⽤Lambda表達式來簡潔實作的接⼝是有要求的。因為Lambda表達式本質上來講,就是⼀個匿名
函數,⽤這個匿名函數來實作接⼝中的⽅法。是以,如果接⼝中有多個必須要實作抽象⽅法時,
Lambda表達式将⽆法是⽤。
注:Lambda表達式要求接⼝中隻有⼀個必須要實作的抽象⽅法。但是JAVA 8對接⼝也有⼀個
拓展。現在,我們可以在接⼝中,是⽤default來修飾⼀個⽅法,此時,這個⽅法需要有實
現。那麼,實作類在實作接⼝的時候,對于default⽅法,可以實作,也可以不實作。是以,
這⾥所說的接⼝中隻有⼀個必須實作的抽象⽅法,與default⽅法⽆關。
@FunctionalInterface**
@FunctionalInterface
因為Lambda表達式要求接⼝中有且隻能有⼀個必須實作的抽象⽅法,是以,對接⼝可以使⽤
@FunctionalInterface接⼝來進⾏限定。這個注解限制了接⼝中隻能有⼀個必須實作的抽象⽅
法。使⽤這個注解修飾的接⼝,⼜叫做函數式接⼝。
⼆、Lambda表達式基礎文法
Lambda表達式,本質上就是⼀個匿名⽅法,是以離不開⽅法的⼏個必要的組成部分:傳回值、⽅法
名、參數、⽅法體。但是由于他是⼀個匿名⽅法,是以⽅法名可以忽略。同時,Lambda中也不需要
顯式聲明傳回值類型。是以,我們在寫Lambda表達式的時候,隻需要關⼼參數和⽅法體即可。
參數: 以()包圍起來,多個參數以逗号分隔
(int a, int b)
⽅法體: 以{}包圍起來
{ System.out.println("hello world"); }
->: Lambda運算符,⽤來分隔參數和⽅法體
(int a, int b) -> {};
有了這⼏個組成部分,我們就可以對任意的函數式接⼝使⽤Lambda進⾏實作
// ⽆參、⽆傳回值
() -> { System.out.println("hello world"); };
// int, int參數、⽆傳回值
(int a, int b) -> { System.out.println(a + b); };
// int, int參數、int傳回值
(int a, int b) -> { return a + b; };
三、Lambda表達式文法精簡
接⼝中定義的⽅法,已經聲明了⽅法的參數類型、數量和傳回值類型。是以,使⽤Lambda表達式在
實作的時候,對應的部分可以省略參數精簡
- 參數的類型可以精簡
(int a, int b) -> { System.out.println(a + b); }
可以精簡為:
(a, b) -> { System.out.println(a + b); }
- 如果參數數量隻有⼀個,⼩括号可以精簡
(int a) -> { System.out.println(a); }
a -> { System.out.println(a); }
⽅法體精簡
- 如果⽅法體中隻有⼀條語句,則⼩括号可以省略
a -> System.out.println(a);
- 如果⽅法體中唯⼀的⼀條語句是傳回值,在精簡掉⼤括号後,return也必須省略
a -> { return a * 2; }
a -> a * 2;
四、Lambda表達式文法進階之⽅法引⽤
什麼是⽅法引⽤
如果在使⽤Lambda進⾏接⼝實作的時候,需要實作的邏輯已經在某⼀個⽅法中實作,則可以直接使
⽤⽅法引⽤,指向指定的⽅法。
interface Calculate {
int calculate(int a, int b);
public class Program {
// 接⼝引⽤
Calculate c = (a, b) -> a + b;
public static int add(int a, int b) {
return a + b;
}在上述代碼中,main函數中Calculate引⽤c的實作部分,下⾯已經有⼀個⽅法 add 進⾏了實作。
是以此時,我們不需要再實作⼀次,隻需要直接指向已經寫好的實作即可。是以,可以進⾏如下改
造:
Calculate c = Program::add;
上⾯的 Program::add 就是⼀個⽅法引⽤。引⽤了Program類中的⼀個靜态⽅法add。
在使⽤⽅法引⽤的時候需要注意
- 引⽤的⽅法參數數量、參數類型、傳回值類型必須和函數式接⼝中的⽅法定義⼀緻。
- ⽅法引⽤必須有引⽤主體,即⽅法的⾪屬者。例如:上⽅的add⽅法是⼀個靜态⽅法,需要使⽤
類來調⽤。是以⽅法引⽤就是 類::⽅法,如果是⼀個成員⽅法,則需要使⽤ 對象::⽅法 的
形式來引⽤。
構造⽅法的引⽤
如果需要引⽤⼀個構造⽅法,需要使⽤ 類::new 進⾏引⽤
interface CreatePerson {
Person getPerson();
class Person {}
CreatePerson c = Person::new;
五、Lambda表達式之綜合案例: 排序Comparator
// 排序
list.sort((o1, o2) -> o2.age - o1.age);
// 使⽤Lambda表達式來實作Comparator接⼝,并執行個體化⼀個TreeSet對象
TreeSet set = new TreeSet<>((o1, o2) -> {
if (o1.age >= o2.age) {
return -1;
else {
return 1;
});
六、Lambda表達式之綜合案例: forEach()// 将集合中的每⼀個元素都帶⼊到⽅法accept中。
list.forEach(System.out::println);
// 輸出集合中所有的偶數
list.forEach(ele -> {
if (ele % 2 == 0) {
System.out.println(ele);
七、Lambda表達式之綜合案例: removeIf()
// 将集合中的每⼀個元素都帶⼊到test⽅法中, 如果傳回值是true,則删除這個元素
// 删除集合中的年齡⼤于10歲的元素
list.removeIf(ele -> ele.age > 10);
⼋、Lambda表達式之綜合案例: 線程執行個體化
new Thread(() -> {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}).start();
九、系統内置函數式接⼝
系統已經給我們提供了很多函數式接⼝,⽅便我們的使⽤。是以,如果我們需要⽤到以下⽅法的時
候,不需要再設計接⼝,直接使⽤指定的接⼝即可。函數式接⼝
參
數
傳回值 特殊說明:⼏個特殊實作的⼦接⼝
Predicate T boolean
IntPredicate :參數:int,傳回值:boolean
LongPredicate :參數:long,傳回值:boolean
DoublePredicate :參數:double,傳回值:boolean
Consumer T void
IntConsumer :參數:int,傳回值:void LongConsumer :
參數:int,傳回值:void DoubleConsumer :參數:int,返
回值:void
Function T R
IntFunction :參數:int,傳回值:R
IntToDoubleFunction :參數:int,傳回值:double
IntToLongFunction :參數:int,傳回值:long
LongFunction :參數:long,傳回值:R
LongToDoubleFunction :參數:long,傳回值:double
LongToIntFunction :參數:long,傳回值:int
DoubleFunction :參數:double,傳回值:R
DoubleToIntFunction :參數:double,傳回值:int
DoubleToLongFunction :參數:double,傳回值:long
Supplier ⽆ T
BooleanSupplier :參數:⽆,傳回值:boolean
IntSupplier :參數:⽆,傳回值:int LongSupplier :參
數:⽆,傳回值:long DoubleSupplier :參數:⽆,傳回值:
double
UnaryOperator T T
IntUnaryOperator :參數:int,傳回值:int
LongUnaryOperator :參數:long,傳回值:long
DoubleUnaryOperator :參數:double,傳回值:double
BinaryOperator T,
T T
IntBinaryOperator :參數:int, int,傳回值:int
LongBinaryOperator :參數:long, long,傳回值:long
DoubleBinaryOperator :參數:double, double,傳回值:
BiPredicate
R>
L,
R boolean
BiConsumer
U>
T,
U void
BiFunction
U, R>
U R
上述接⼝中,最常⽤的是 Predicate、Consumer、