在本文中惦费,我們將通過示例了解Javalambda表達(dá)式以及l(fā)ambda表達(dá)式在函數(shù)接口、通用函數(shù)接口和流API中的使用。
lambda表達(dá)式是在Java8中首次引入的掀鹅。它的主要目標(biāo)是提高語言的表達(dá)能力揽碘。
但是次屠,在進(jìn)入lambdas之前,我們首先需要了解功能接口雳刺。
什么是Functional Interface劫灶?
如果Java接口包含一個(gè)且僅包含一個(gè)抽象方法,則稱為函數(shù)接口掖桦。只有一種方法指定了接口的預(yù)期用途本昏。
例如,包java.lang
中的Runnable
接口枪汪;是一個(gè)函數(shù)接口涌穆,因?yàn)樗粯?gòu)成一個(gè)方法,即run()
雀久。
示例1:用java定義函數(shù)接口
import java.lang.FunctionalInterface;
@FunctionalInterface
public interface MyInterface{
// the single abstract method
double getValue();
}
在上面的示例中宿稀,接口MyInterface
只有一個(gè)抽象方法getValue()
。因此赖捌,它是一個(gè)功能接口祝沸。
這里,我們使用了注釋@functioninterface
越庇。注釋強(qiáng)制Java編譯器指示該接口是功能接口罩锐。因此,不允許有多個(gè)抽象方法卤唉。然而涩惑,這不是強(qiáng)制性的。
在Java7中桑驱,函數(shù)接口被視為單一抽象方法或SAM類型竭恬。SAMs通常在Java7中使用匿名類實(shí)現(xiàn)。
示例2:用java中的匿名類實(shí)現(xiàn)SAM
public class FunctionInterfaceTest {
public static void main(String[] args) {
// anonymous class
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("I just implemented the Runnable Functional Interface.");
}
}).start();
}
}
輸出:
just implemented the Runnable Functional Interface.
在這里熬的,我們可以將一個(gè)匿名類傳遞給一個(gè)方法萍聊。這有助于在Java7中編寫代碼更少的程序。但是悦析,語法仍然很難寿桨,需要很多額外的代碼行。
Java8進(jìn)一步擴(kuò)展了SAMs的功能。因?yàn)槲覀冎篮瘮?shù)接口只有一個(gè)方法亭螟,所以在將其作為參數(shù)傳遞時(shí)挡鞍,不需要定義該方法的名稱。Lambda表達(dá)式允許我們完全做到這一點(diǎn)预烙。
Lambda表達(dá)式簡介
Lambda表達(dá)式本質(zhì)上是一個(gè)匿名或未命名的方法墨微。lambda表達(dá)式不會(huì)自行執(zhí)行。相反扁掸,它用于實(shí)現(xiàn)由函數(shù)接口定義的方法翘县。
如何在Java中定義lambda表達(dá)式?
下面是如何在Java中定義lambda表達(dá)式谴分。
(parameter list) -> lambda body
使用的新運(yùn)算符(->)
稱為箭頭運(yùn)算符或lambda運(yùn)算符锈麸。語法目前可能不清楚。讓我們來探討一些例子牺蹄,
假設(shè)我們有這樣一種方法:
double getPiValue() {
return 3.1415;
}
我們可以使用lambda表達(dá)式編寫此方法忘伞,如下所示:
() -> 3.1415
這里,該方法沒有任何參數(shù)沙兰。因此氓奈,運(yùn)算符的左側(cè)包含一個(gè)空參數(shù)。右側(cè)是lambda主體鼎天,指定lambda表達(dá)式的操作舀奶。在本例中,它返回值3.1415斋射。
Lambda體的類型
在Java中育勺,lambda主體有兩種類型。
- 具有單個(gè)表達(dá)式的主體
() -> System.out.println("Lambdas are great");
這種類型的lambda體稱為表達(dá)式體绩鸣。
- 由代碼塊組成的主體怀大。
() -> {
double pi = 3.1415;
return pi;
};
這種類型的lambda體稱為塊體纱兑。塊體允許lambda體包含多個(gè)語句呀闻。這些語句包含在大括號(hào)內(nèi),必須在大括號(hào)后添加分號(hào)潜慎。
注意:對(duì)于塊體捡多,如果塊體返回值,則可以使用return語句铐炫。但是垒手,表達(dá)式體不需要返回語句。
示例3:Lambda表達(dá)式
讓我們編寫一個(gè)Java程序倒信,使用lambda表達(dá)式返回Pi的值科贬。
如前所述,lambda表達(dá)式不會(huì)單獨(dú)執(zhí)行。相反榜掌,它形成了由函數(shù)接口定義的抽象方法的實(shí)現(xiàn)优妙。
因此,我們需要首先定義一個(gè)功能接口憎账。
import java.lang.FunctionalInterface;
// this is functional interface
@FunctionalInterface
interface MyInterface{
// abstract method
double getPiValue();
}
public class Main {
public static void main( String[] args ) {
// declare a reference to MyInterface
MyInterface ref;
// lambda expression
ref = () -> 3.1415;
System.out.println("Value of Pi = " + ref.getPiValue());
}
}
輸出:
Value of Pi = 3.1415
在上面的例子中套硼,
我們已經(jīng)創(chuàng)建了一個(gè)名為MyInterface
的功能接口。它包含一個(gè)名為getPiValue()
的抽象方法
在主類中胞皱,我們聲明了對(duì)MyInterface
的引用邪意。注意,我們可以聲明接口的引用反砌,但不能實(shí)例化接口雾鬼。就是,
// it will throw an error
MyInterface ref = new myInterface();
// it is valid
MyInterface ref;
然后于颖,我們?yōu)橐弥付艘粋€(gè)lambda表達(dá)式呆贿。
ref = () -> 3.1415;
最后,我們使用引用接口調(diào)用方法getPiValue()
System.out.println("Value of Pi = " + ref.getPiValue());
帶參數(shù)的Lambda表達(dá)式
到目前為止森渐,我們已經(jīng)創(chuàng)建了沒有任何參數(shù)的lambda表達(dá)式做入。但是,與方法類似同衣,lambda表達(dá)式也可以有參數(shù)竟块。例如《求職面試筆試寶典》
(n) -> (n%2)==0
這里,括號(hào)內(nèi)的變量n是傳遞給lambda表達(dá)式的參數(shù)耐齐。lambda主體接受參數(shù)并檢查它是偶數(shù)還是奇數(shù)浪秘。
示例4:使用帶參數(shù)的lambda表達(dá)式
@FunctionalInterface
interface MyInterface {
// abstract method
String reverse(String n);
}
public class Main {
public static void main( String[] args ) {
// declare a reference to MyInterface
// assign a lambda expression to the reference
MyInterface ref = (str) -> {
String result = "";
for (int i = str.length()-1; i >= 0 ; i--)
result += str.charAt(i);
return result;
};
// call the method of the interface
System.out.println("Lambda reversed = " + ref.reverse("Lambda"));
}
}
輸出:
Lambda reversed = adbmaL
通用功能接口
到目前為止,我們使用的函數(shù)接口只接受一種類型的值埠况。例如
@FunctionalInterface
interface MyInterface {
String reverseString(String n);
}
上面的函數(shù)接口只接受字符串并返回字符串耸携。但是,我們可以使函數(shù)接口通用辕翰,以便接受任何數(shù)據(jù)類型夺衍。如果您不確定泛型,請(qǐng)?jiān)L問Java泛型喜命。
示例5:通用函數(shù)接口和Lambda表達(dá)式
// GenericInterface.java
@FunctionalInterface
interface GenericInterface<T> {
// generic method
T func(T t);
}
// GenericLambda.java
public class Main {
public static void main( String[] args ) {
// declare a reference to GenericInterface
// the GenericInterface operates on String data
// assign a lambda expression to it
GenericInterface<String> reverse = (str) -> {
String result = "";
for (int i = str.length()-1; i >= 0 ; i--)
result += str.charAt(i);
return result;
};
System.out.println("Lambda reversed = " + reverse.func("Lambda"));
// declare another reference to GenericInterface
// the GenericInterface operates on Integer data
// assign a lambda expression to it
GenericInterface<Integer> factorial = (n) -> {
int result = 1;
for (int i = 1; i <= n; i++)
result = i * result;
return result;
};
System.out.println("factorial of 5 = " + factorial.func(5));
}
}
輸出:
Lambda reversed = adbmaL
factorial of 5 = 120
在上面的示例中沟沙,我們創(chuàng)建了一個(gè)名為GenericInterface
的通用函數(shù)接口。它包含一個(gè)名為func()
的泛型方法壁榕。
在這里矛紫,在主類中,
-
GenericInterface<String>reverse-
創(chuàng)建對(duì)接口的引用牌里。該接口現(xiàn)在對(duì)字符串類型的數(shù)據(jù)進(jìn)行操作颊咬。 -
GenericInterface<Integer>factorial-
創(chuàng)建對(duì)接口的引用。在本例中,接口對(duì)整型數(shù)據(jù)進(jìn)行操作喳篇。
Lambda表達(dá)式和StreamAPI
新的java.util.stream
包已添加到JDK8中缓呛,它允許java開發(fā)人員執(zhí)行搜索、篩選杭隙、映射哟绊、減少或操作列表等集合。
例如痰憎,我們有一個(gè)數(shù)據(jù)流(在本例中是一個(gè)字符串列表)票髓,其中每個(gè)字符串都是國家名稱和國家地點(diǎn)的組合。現(xiàn)在铣耘,我們可以處理這些數(shù)據(jù)流洽沟,只從尼泊爾檢索地方。
為此蜗细,我們可以通過stream API和Lambda表達(dá)式的組合在stream中執(zhí)行批量操作裆操。
示例6:演示將lambdas與stream API一起使用
import java.util.ArrayList;
import java.util.List;
public class StreamMain {
// create an object of list using ArrayList
static List<String> places = new ArrayList<>();
// preparing our data
public static List getPlaces(){
// add places and country to the list
places.add("Nepal, Kathmandu");
places.add("Nepal, Pokhara");
places.add("India, Delhi");
places.add("USA, New York");
places.add("Africa, Nigeria");
return places;
}
public static void main( String[] args ) {
List<String> myPlaces = getPlaces();
System.out.println("Places from Nepal:");
// Filter places from Nepal
myPlaces.stream()
.filter((p) -> p.startsWith("Nepal"))
.map((p) -> p.toUpperCase())
.sorted()
.forEach((p) -> System.out.println(p));
}
}
輸出:
Places from Nepal:
NEPAL, KATHMANDU
NEPAL, POKHARA
在上面的示例中,請(qǐng)注意以下語句:
myPlaces.stream()
.filter((p) -> p.startsWith("Nepal"))
.map((p) -> p.toUpperCase())
.sorted()
.forEach((p) -> System.out.println(p));
這里炉媒,我們使用流API的filter()
踪区、map()
和forEach()
等方法。這些方法可以將lambda表達(dá)式作為輸入吊骤。
我們還可以根據(jù)上面學(xué)習(xí)的語法定義自己的表達(dá)式缎岗。這使我們能夠像上面的示例中所看到的那樣,大幅減少代碼行白粉。