Lambda 表達式,也可稱為閉包,它是推動 Java 8 發布的最重要新特性。
Lambda 允許把函數作為一個方法的參數(函數作為參數傳遞進方法中)。
使用 Lambda 表達式可以使代碼變的更加簡潔緊湊。
Lambda是數學中的一個函數. Java中使用方法來代替函數,方法總是作為類或對象的一部分存在的。
可以把Lambda看作是一個匿名方法, 擁有更簡潔的語法。
語法:
(參數列表) -> {語句;}
Lambda表達式由參數列表和一個Lambda體組成, 通過箭頭連接。
說明:
● 當只有一個參數時, 參數列表的小括弧可以省略:x -> { System.out.println(x); }
● 參數列表中參數的數據類型可以省略:( x,y ) -> {x.compareTo(y);}
● 如果Lambda體只有一條語句,大括弧也可以省略:x -> { return x + 2 ; }
● 如果Lambda體中只有一條return語句, return關鍵字可以省略:(x,y) -> x+y
以下表達式是有效的Lambda表達式
(String s ) -> s.length()
(Student stu) -> stu.getAge() > 18
(int x, int y ) -> {
System.out.print(“result”);
System.out.println( x + y );
}
() -> {}
() -> “hehe”
(String s) -> { “wkcto” } //不合法
(String s) -> { return “wkcto” ; }
(String s) -> “wkcto”
Lambda使用案例:
布爾表達式, 判斷參數接收的list集合是否為空
(List list) -> list.isEmpty()
創建對象,并返回
() -> new Student()
消費(使用)一個對象, 把參數接收學生對象的姓名打印出來
(Student stu ) -> { System.out.println( stu.name) ; }
從一個對象中選擇, 返回參數對象的成績
(Student stu) -> stu.getScore()
組合兩個值
(int a, int b) -> a*b
比較兩個對象
(Student stu1, Student stu2) -> stu1.getScore() - stu2.getScore()
在JDK8中, 引用了函數式接口. 就是只定義一個抽象方法的接口.如:Comparator接口 , Runnable接口
@FunctionalInterface //注解,聲明接口為函數式接口
public interface Adder{
int add(int x, int y);
}
public interface ByteAdder extends Adder{
byte add( byte b1, byte b2);
}
當前ByteAdder接口不是函數式接口, 從Adder接口中繼承了一個抽象方法,在本接口中又定義了一個抽象方法
public inteface Nothing{
}
當前Nothing接口也不函數式接口,因為沒有抽象方法
函數式接口就是為Lambda表達式準備的,或者說Lambda表達式必須實現一個函數式接口
java.util.function包中定義了一些基本的函數式接口,如Predicate, Consumer, Function,Supplier等
Predicate接口中定義一個抽象方法test(T ),接收一個T類型的對象參數,返回一個布爾值
當需要一個涉及類型T的布爾表達式時,可以使用這個接口
public class Test01 {
public static void main(String[] args) {
List list = Arrays.asList("lisi", "zhangsan", "wangwu", "chenqi", "feifei");
List result = filter(list, x -> x.length() > 6);
System.out.println(result);
}
//定義方法, 方法可以把List列表中符合條件的元素存儲到一個新的List列表中返回
public static List filter(List list, Predicate predicate){
List result = new ArrayList<>();
//遍歷list參數列表,把符合predicate條件的元素存儲到result中
for( T t : list){
if( predicate.test(t)){
result.add(t);
}
}
return result;
}
}
2、Consumer接口
Consumer接口定義了一個accept(T)抽象方法,可以接收一個T類型的對象,沒有返回值. 如果需要訪問類型T的對象,對該對象做一些操作,就可以使用這個接口. 在Collection集合和Map集合中都有forEach(Consumer)方法
public class Test02Consumer {
public static void main(String[] args) {
List list =Arrays.asList("lisi", "gg", "jj", "tuantuan", "daimeir", "timo","XDD");
list.forEach(s -> System.out.println(s) );
Map map = new HashMap<>();
map.put("lisi", 22);
map.put("feifei", 28);
map.put("zhangxiaosan", 20);
map.put("chenqi", 30);
map.forEach((k,v)-> System.out.println(k + "->" + v) );
}
}
3、Function接口
Function接口中定義了accept( T )方法,接收一個T類型的參數對象,返回一個R類型的數據. 如果需要定義一個Lambda,將一個輸入對象的信息加工后映射到輸出,就可以使用該接口
public class Test03Function {
public static void main(String[] args) {
List list = Arrays.asList("lisi", "gg", "jj", "tuantuan", "daimeir", "timo","XDD");
//把list集合中存儲字符串的長度映射出來
List result = map(list, x->x.length() );
System.out.println(result);
}
//把一個List列表映射到另外一個List列表中
public static List map(List list, Function function){
List result = new ArrayList<>(); //存儲映射后的數據
for( T t : list){
result.add( function.apply(t) ); //把apply對t對象的操作結果保存到result集合中
}
return result;
}
}
4、原始類型的處理
泛型只能綁定引用類型,不能使用基本類型。
Java8中函數式接口為基本類型也提供了對應的接口,可以避免在進行輸入輸出原始數據時頻繁進行裝箱/拆箱操作。
一般來說,在針對專門的基本類型數據的函數式接口名稱前面加上了對應的原始類型前綴,如: IntPredicate, IntConsumer, IntFunction等。
IntPredicate evenNumbers = (int x ) -> x % 2 == 0 ;
evenNumbers.test( 10 ); //返回true
evenNumbers.test( 11 ); //返回false
Lambda表達式捕獲
Lambda表達式可以使用外層作用域中定義的變量,如成員變量,局部變量,稱為捕獲Lambda。
package com.wkcto.lambda;
import java.util.function.IntUnaryOperator;
/**
* author: 動力節點老崔
* 2019/3/13
*/
public class Test04LambdaVariable {
int xx = 123; //實例變量
static int yy = 456; //靜態變量
public static void main(String[] args) {
//在Lambda表達式中使用成員變量,當前main方法是靜態方法,只能使用靜態變量
IntUnaryOperator operator = i ->{
return i + yy; //把參數接收的數據與靜態變量的值相加并返回
};
System.out.println( operator.applyAsInt(10));
//在Lambda表達式中使用局部變量,局部變量必須是final修飾,或者是事實上的final
int zz = 789; //局部變量
final int ff = 147; //final修飾的局部變量
IntUnaryOperator operator2 = i->{
return i + ff; //把參數接收的數據與ff的值相加,然后返回
};
System.out.println(operator2.applyAsInt(10));
operator2 = i ->{
return i + zz; //zz雖然沒有使用final修飾,如果它是事實的final,后面沒有修改zz值的代碼
};
System.out.println( operator2.applyAsInt(10));
// zz = 258; //如果再對zz重新賦值,則上面的lambda表達式語法錯誤
}
}