一、概念
從本質(zhì)上來說,它就是一個匿名函數(shù),可以用來直接實(shí)現(xiàn)接口中的方法,從而簡化代碼。但是Lambda有一個限制,不能實(shí)現(xiàn)接口中的所有方法,所以Lambda表達(dá)式只能用于有且僅有一個必須需要實(shí)現(xiàn)的方法接口,這里需要注意必須需要實(shí)現(xiàn)這六個字。
public interface Printer {
//有一個需要實(shí)現(xiàn)的方法,可以使用Lambda表達(dá)式
void print();
}
public interface Printer {
//有一個需要實(shí)現(xiàn)的方法,可以使用Lambda表達(dá)式
void print();
//這里雖然有一個方法,但接口提供了默認(rèn)實(shí)現(xiàn),因此不是必須要實(shí)現(xiàn)的
default void printDetail(){}
}
public interface Printer {
//有一個需要實(shí)現(xiàn)的方法,可以使用Lambda表達(dá)式
void print();
//這里雖然有一個需要實(shí)現(xiàn)的方法,但不是必須要實(shí)現(xiàn)的,因?yàn)閠oString()是Object類中的.
String toString();
}
public interface Printer {
//有一個需要實(shí)現(xiàn)的方法,可以使用Lambda表達(dá)式
void print();
//這里雖然有一個需要實(shí)現(xiàn)的方法,但不是必須要實(shí)現(xiàn)的,因?yàn)閠oString()是Object類中的.
String toString();
}
像這種只有一個必須要實(shí)現(xiàn)的方法的接口,在java8中稱之為函數(shù)式接口,在定義接口時可以在接口名上方加上@FunctionInterface
標(biāo)簽,用于驗(yàn)證此接口是否為函數(shù)式接口。如果這個接口定義好之后不是函數(shù)式接口,那么接口名處會報(bào)錯。
在使用Lambda表達(dá)式的時候,不需要關(guān)注方法名,只需要關(guān)注方法參數(shù)和返回值即可。基本語法很簡單:
(參數(shù)列表)->{
方法體
};
二、用法比較
java中實(shí)現(xiàn)接口的方式在java8之前有兩種:定義接口的實(shí)現(xiàn)類,使用匿名類,但Lambda表達(dá)式相比于這種方法都簡單很多。以上文的Printer接口為例,實(shí)現(xiàn)如下:
2.1 實(shí)現(xiàn)類
class PrinterImpl implements Printer{
@Override
public void print() {
System.out.println("Hello World");
}
}
2.2 匿名類
class PrinterAnonymous {
Printer printer = new Printer() {
@Override
public void print() {
System.out.println("Hello World");
}
};
}
2.3 Lambda
class PrinterLambda{
Printer p = ()-> System.out.println("Hello World");
}
比較上文三種實(shí)現(xiàn)方式,很顯示Lambda的實(shí)現(xiàn)比前兩種簡單很多。
三、基本用法
3.1 無參數(shù)無返回值接口方法
@FunctionalInterface
public interface Printer {
void print();
}
public class Tester {
public static void main(String[] args) {
// 方法一,無返回值的情況,方法體只有一條語句,可以省略大括號
Printer p1 = () -> System.out.println("Hello World 1");
p1.print();
// 方法二,標(biāo)準(zhǔn)定義
Printer p2 = () -> {
System.out.println("Hello World 2");
};
p2.print();
}
}
3.2 一個參數(shù)無返回值接口方法
@FunctionalInterface
public interface Printer {
void print(String str);
}
public class Tester {
public static void main(String[] args) {
// 方法一,無返回值的情況,方法體只有一條語句,可以省略大括號
//因?yàn)檫@里只有一個參數(shù),小括號也可以省略,小括號省略的前提是:有且僅有一個參數(shù)
//Printer p1 = s -> System.out.println(s);
Printer p1 = (s) -> System.out.println(s);
p1.print("Hello World 1");
// 方法二,無返回值的情況,方法體只有一條語句,可以省略大括號
Printer p2 = (String s) -> System.out.println(s);
p2.print("Hello World 2");
// 方法三,標(biāo)準(zhǔn)定義
Printer p3 = (String s) -> {
System.out.println(s);
};
p3.print("Hello World 3");
}
}
3.3 多個參數(shù)無返回值接口方法
@FunctionalInterface
public interface Printer {
void print(String str1,String str2);
}
public class Tester {
public static void main(String[] args) {
// 方法一,無返回值的情況,方法體只有一條語句,可以省略大括號
//參
Printer p1 = (s1,s2) -> System.out.println(s1+" "+s2);
p1.print("Hello World 1","Java 1");
// 方法二,無返回值的情況,方法體只有一條語句,可以省略大括號
Printer p2 = (String s1,String s2) -> System.out.println(s1+" "+s2);
p2.print("Hello World 2","Java 2");
// 方法三,標(biāo)準(zhǔn)定義
Printer p3 = (String s1,String s2) -> {
System.out.println(s1+" "+s2);
};
p3.print("Hello World 3","Java 3");
}
}
3.4 無參數(shù)有返回值接口方法
@FunctionalInterface
public interface Printer {
boolean print();
}
public class Tester {
public static void main(String[] args) {
// 方法一,有返回值的情況,只有一條語句,return關(guān)鍵字的有無決定能否活力大括號
Printer p1 = () -> true;
boolean has1 = p1.print();
System.out.println(has1);//測試返回結(jié)果
// 方法二,標(biāo)準(zhǔn)定義
Printer p2 = () -> {return true;};
boolean has2 = p2.print();
System.out.println(has2);//測試返回結(jié)果
}
}
3.5 一個參數(shù)有返回值接口方法
@FunctionalInterface
public interface Printer {
boolean print(boolean good);
}
public class Tester {
public static void main(String[] args) {
// 方法一,有返回值的情況,只有一條語句,return關(guān)鍵字的有無決定能否活力大括號
//因?yàn)檫@里只有一個參數(shù),小括號也可以省略,小括號省略的前提是:有且僅有一個參數(shù)
//Printer p1 = good -> good;
Printer p1 = (good) -> good;
boolean has1 = p1.print(true);
System.out.println(has1);
// 方法二,標(biāo)準(zhǔn)定義
Printer p2 = (good) -> {return good;};
boolean has2 = p2.print(false);
System.out.println(has2);
}
}
3.6 多個參數(shù)有返回值接口方法
@FunctionalInterface
public interface Printer {
boolean print(boolean good1,boolean good2);
}
public class Tester {
public static void main(String[] args) {
// 方法一,有返回值的情況,只有一條語句,return關(guān)鍵字的有無決定能否活力大括號
Printer p1 = (good1,good2) -> good1;
boolean has1 = p1.print(true,false);
System.out.println(has1);
// 方法二,標(biāo)準(zhǔn)定義
Printer p2 = (good1,good2) -> {return good1;};
boolean has2 = p2.print(false,false);
System.out.println(has2);
}
}
四、函數(shù)引用
在實(shí)現(xiàn)一個接口的方法時,如果現(xiàn)有的其他地方的某個函數(shù)已經(jīng)實(shí)現(xiàn)了接口方法的邏輯,可以使用方法引用直接將這個邏輯引用過來。
4.1 靜態(tài)方法引用
語法:
接口名 變量名 = 類 ::已實(shí)現(xiàn)的方法
注意事項(xiàng):
- 在引用的方法后面,不要添加小括號
- 引用的這個方法,參數(shù)和返回值,必須要跟接口中定義的一致
示例:
Printer 需要實(shí)現(xiàn)的方法在Checker中有同樣的實(shí)現(xiàn),這樣就可以直接引用過來
@FunctionalInterface
public interface Printer {
String print(boolean good1,boolean good2);
}
public class Checker {
public static String check(boolean a,boolean b) {
if(a && b) {
return "Java is good";
}else if (!a && b) {
return "Java is better";
}
return "Java is best";
}
}
public class Tester {
public static void main(String[] args) {
Printer p1 = Checker::check;//用類名來引用
System.out.println(p1.print(true, true));
}
}
4.2 非靜態(tài)方法引用
語法:
接口名 變量名 = 對象 ::靜態(tài)方法
注意事項(xiàng):
- 在引用的方法后面,不要添加小括號
- 引用的這個方法,參數(shù)和返回值,必須要跟接口中定義的一致
示例:
Printer 需要實(shí)現(xiàn)的方法在Checker中有同樣的實(shí)現(xiàn),這樣就可以直接引用過來
@FunctionalInterface
public interface Printer {
String print(boolean good1,boolean good2);
}
public class Checker {
public String check(boolean a,boolean b) {
if(a && b) {
return "Java is good";
}else if (!a && b) {
return "Java is better";
}
return "Java is best";
}
}
public class Tester {
public static void main(String[] args) {
Printer p1 = new Checker()::check;//必須用對象來引用
System.out.println(p1.print(true, true));
}
}
4.3 構(gòu)造方法的引用
如果一個函數(shù)式接口中定義的方法僅僅是為了得到一個對象,此時我們就可以使用構(gòu)造方法的引用,簡化這個方法的實(shí)現(xiàn)
語法:
接口名 變量名 = 類名 ::new
注意事項(xiàng):
可以通過接口中的方法參數(shù),區(qū)分引用不同的構(gòu)造方法
示例:
@FunctionalInterface
public interface Printer1 {
Checker getCheck();
}
@FunctionalInterface
public interface Printer2 {
Checker getCheck(int a);
}
public class Checker {
int times;
public Checker() {
System.out.println("I am none parameter");
}
public Checker(int a) {
System.out.println("I have one parameter");
}
}
public class Tester {
public static void main(String[] args) {
//引用無參構(gòu)造方法
Printer1 p1 = Checker::new;
p1.getCheck();
//引用有參構(gòu)造方法
Printer2 p2 = Checker::new;
p2.getCheck(1);
}
}
4.4 對象方法的特殊引用
如果實(shí)現(xiàn)某些接口的時候,Lambda表達(dá)式中包含了某一個對象,此時方法體中,直接使用這個對象調(diào)用它的某一個方法就可以完成整個的邏輯。其他的參數(shù),可以作為調(diào)用方法的參數(shù)。此時,可以對這種實(shí)現(xiàn)進(jìn)行簡化。
示例:
@FunctionalInterface
public interface Printer1 {
int getCheck(Checker checker);
}
@FunctionalInterface
public interface Printer2 {
void setCheck(Checker checker, int a);
}
public class Tester {
public static void main(String[] args) {
Checker checker = new Checker();
checker.setTimes(100);
// 沒有簡化前,按照之前的方法使用lambda表達(dá)式
Printer1 p1 = x -> x.getTimes();
System.out.println(p1.getCheck(checker));//測試
// 簡化之后
Printer1 p11 = Checker::getTimes;
System.out.println(p11.getCheck(checker));//測試
// 沒有簡化前,按照之前的方法使用lambda表達(dá)式
Printer2 p2 = (x,y)-> x.setTimes(y);
p2.setCheck(checker, 50);
System.out.println(checker.getTimes());//測試
// 簡化之后
Printer2 p22 = Checker::setTimes;
p22.setCheck(checker, 30);
System.out.println(checker.getTimes());//測試
}
}
五、注意
當(dāng)在Lambda表達(dá)式中使用了某一個局部變量,那么這個局部變量的值在Lambda表達(dá)式之外,不可以被改變,因?yàn)槟J(rèn)將其定義成final常量。但全局變量變量沒有這方面的限制。
示例:
@FunctionalInterface
public interface Printer {
void setTime();
}
public class Tester {
public static void main(String[] args) {
int time = 10;
Printer p = () -> System.out.println(time);//這里出錯了,因?yàn)橄乱恍袑ime進(jìn)行修改
time = 15;//這里的值不能改變,會導(dǎo)致上一行出錯
}
}
基本概括了Lambda表達(dá)式的所有用法,不足之處,請諒解,謝謝!
本篇關(guān)于 java8 新特性中的匿名函數(shù) lambda 表達(dá)式詳細(xì)知識內(nèi)容的文章介紹到此就結(jié)束了,想要了解更多相關(guān) java lambda表達(dá)式其他方面的應(yīng)用內(nèi)容請搜索W3Cschool以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持我們!