Lambda表達式由參數(shù)、箭頭和主體組成
(Apple a1,Apple a2)-> a1.getWeight().compareTo(a2.getWeight());
1.lambda是什么
可以把Lambda表達式理解為簡潔的表示可傳遞的匿名函數(shù)一種方式:它沒有名稱拼岳,但是它有參數(shù)列表塞关、函數(shù)主體晕讲、返回類型贵白,可能還有一個可以拋出的異常列表
(String s)->s.length(); // 參數(shù)類型為Sting抚芦,返回值為int型倍谜;lambda 沒有return語句,隱含return
(Apple a) ->a.getWeight()>150; //參數(shù)類型為Apple燕垃,返回值為boolean類型(判斷蘋果的重量是否大于150g)
(int x,int y)->{
System.out.println("Result:");
System.out.println(x+y);
}; //參數(shù)類型為兩個int類型數(shù)據(jù)枢劝,沒有返回值(void);lambda可以包含多行語句
()->42; //沒有參數(shù)卜壕,返回值為int類型
(Apple a1,Apple a2) ->a1.getWeight().compareTo(a2.getWeight()); // 參數(shù)為2個Apple類型數(shù)據(jù)您旁,返回值為int型
2.函數(shù)式接口:就是只定義一個抽象方法的接口
public interface Runnable{
void run();
}
Lambda允許你直接以內(nèi)聯(lián)的形式為函數(shù)式接口的抽象方法提供實現(xiàn)并把整個表達式作為函數(shù)式接口的實例,即lambda是函數(shù)式接口一個具體實現(xiàn)的實例轴捎。
public static void main(String[] args){
Runnable r1 = ()->System.out.println("Hello World 1");
Runnable r2 = new Runnable() {
@Override
public void run() {
System.out.println("Hello World 2");
}
};
process(r1); //使用lambda表達式
process(r2); //使用匿名類
process(()->System.out.println("Hello world 3")); //使用直接傳遞的lambda打印
}
public static void process(Runnable r){
r.run();
}
關于@FunctionalInterface
該注解不是必須的鹤盒,如果一個接口符合"函數(shù)式接口"定義,那么加不加該注解都沒有影響侦副。加上該注解能夠更好地讓編譯器進行檢查侦锯。如果編寫的不是函數(shù)式接口,但是加上了@FunctionInterface秦驯,那么編譯器會報錯
3.函數(shù)描述符:函數(shù)式接口的抽象方法的簽名稱為尺碰,這種抽象方法叫做函數(shù)描述符
4.常用函數(shù)式接口
(1)Predicate
函數(shù)描述符為: T->boolean 表示參數(shù)為泛型,返回值為boolean類型,當你需要表示一個需要涉及類型T的布爾表達式時亲桥,就可以使用這個接口洛心。
//創(chuàng)建一個函數(shù)式接口
public interface Predicate<T> {
boolean test(T t);
}
//抽象方法
public static <T> List<T> filter(List<T> list,Predicate<T> p){
List<T> results = new ArrayList<>();
for (T s:list){
if(p.test(s)){
results.add(s);
}
}
return results;
}
//調(diào)用方法(分步驟)
Predicate<String> nonEmptyStringPredicate = (String s )-> !s.isEmpty();
List<String> nonEmpty = filter(listOfStrings,nonEmptyStringPredicate);
//也可以寫成
List<String> nonEmpty = filter(listOfStrings, (String s )-> !s.isEmpty(););
注意:屬于java.util.function.Predicate<T>
(2)Consumer
函數(shù)描述符為:T->void 表示參數(shù)類型為泛型,無返回值题篷,當你需要訪問T類型的對象词身,并對其執(zhí)行某些操作,但不需要返回值時就使用這個接口番枚。
//創(chuàng)建函數(shù)式接口
public interface Consumer<T> {
void accept(T t);
}
//抽象方法
public static <T> void forEach(List<T> list,Consumer<T> c){
for(T i:list){
c.accept(i);
}
}
//調(diào)用方法
forEach(Arrays.asList(1,2,3,4,5),(Integer i) -> System.out.println(i));
注意:屬于java.util.function.Consumer<T>
(3)Function
函數(shù)描述符:T->R法严,表示參數(shù)類型為泛型,返回值類型也為泛型葫笼,當你需要將輸入的對象的信息映射到輸出就可以使用這個接口深啤,比如輸入一個對象“蘋果”,想要得到它的重量渔欢,就可以使用這個函數(shù)墓塌。換句話說就是輸入一個泛型,可以得到另一個泛型的值奥额。
//創(chuàng)建函數(shù)式接口
public interface Function<T,R> {
R apply(T t);
}
//抽象方法
public static <T,R> List<R> map(List<T> list,Function<T,R> f){
List<R> result = new ArrayList<>();
for(T s:list){
result.add(f.apply(s));
}
return result;
}
//調(diào)用方法
List<Integer> list =map(Arrays.asList("Predicate","Consumer","Function"),(String s)->s.length());
注意:屬于java.util.function.Function<T,R>
5.原始類型特化
java有兩種類型,引用類型和基本類型访诱,由于泛型只能綁定到引用類型垫挨,這是由于泛型內(nèi)部的實現(xiàn)方式導致的。而java中又有一個自動裝箱和自動拆箱機制 触菜,這兩種操作是自動完成的九榔,所以我們使用上面提到的函數(shù)式接口時,就會在性能上面付出很大的代價涡相。
裝箱:將原始類型(基本類型)轉(zhuǎn)換為對應的引用類型
拆箱:將引用類型轉(zhuǎn)換為對應的基本類型
回顧下之前的知識:基本數(shù)據(jù)類型和引用類型的區(qū)別主要在于基本數(shù)據(jù)類型是分配在棧上的哲泊,而引用類型是分配在堆上的
比如看下面一段代碼:
Predicate<Integer> oddNumbers= (Integer i) ->i%2==1;
oddNumbers.test(1000);
這段代碼就將參數(shù)10000自動裝箱到一個Integer對象中,這時候我們要避免將int型自動裝箱至Integer對象中催蝗,就可以使用Predicate<T>的原始類型特化接口IntPredicate
IntPredicate newNumbers=(int i)->i%2==0;
newNumbers.test(1000);
這段代碼中參數(shù)1000仍為int類型切威。