您好,登錄后才能下訂單哦!
本篇內容介紹了“怎么掌握Java中的Lambda”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
方法類型
從Java 8起方法就是一等公民了。按照標準的定義,編程語言中的一等公民是一個具有下列功能的實體,
可以作為參數進行傳遞, 可以作為方法的返回值 可以賦值給一個變量.
在Java中,每一個參數、返回值或變量都是有類型的,因此每個一等公民都必須是有類型的。Java中的一種類型可以是以下內容之一:
一種內建類型 (比如 int 或者 double) 一個類 (比如ArrayList) 一個接口 (比如 Iterable)
方法是通過接口進行定義類型的。它們不隱式的實現特定接口,但是在必要的時候,如果一個方法符合一個接口,那么在編譯期間,Java編譯器會對其進行隱式的檢查。舉個例子說明:
class LambdaMap {static void oneStringArgumentMethod(String arg) {System.out.println(arg);}}
關于oneStringArgumentMethod函數的類型,與之相關的有:它的的函數是靜態的,返回類型是void,它接受一個String類型的參數。一個靜態函數符合包含一個apply函數的接口,apply函數的簽名相應地符合這個靜態函數的簽名。
oneStringArgumentMethod函數對應的接口因此必須符合下列標準。
它必須包含一個名為apply的函數。 函數返回類型必須是void。 函數必須接受一個String類型可以轉換到的對象的參數。
在符合這個標準的接口之中,下面的這個是最明確的:
interface OneStringArgumentInterface {void apply(String arg);}
利用這個接口,函數可以分配給一個變量:
OneStringArgumentInterface meth = LambdaMap::oneStringArgumentMethod;
用這種方法使用接口作為類型,函數可以借此被分配給變量,傳遞參數并且從函數返回:
static OneStringArgumentInterface getWriter() {return LambdaMap::oneStringArgumentMethod;}static void write(OneStringArgumentInterface writer, String msg) {writer.apply(msg);}
最終函數是一等公民。
泛型函數類型
就像使用集合一樣,泛型為函數類型增加了大量的功能和靈活性。實現功能上的算法而不考慮類型相關信息,泛型函數類型使其變為可能。在對map函數的實現中,會在下面用到這種功能。
在這提供的OneStringArgumentInterface一個泛型版本:
interface OneArgumentInterface<T> {void apply(T arg);}
OneStringArgumentInterface函數可以被分配給它:
OneArgumentInterface<String> meth = LambdaMap::oneStringArgumentMethod;
通過使用泛型函數類型,它現在可以以一種通用的方法實現算法,就像它在集合中使用的一樣:
static <T> void applyArgument(OneArgumentInterface<T> meth, T arg) {meth.apply(arg);}
上面的函數并沒有什么用,然而它至少可以提出一個想法:對函數作為第一個類成員的支持怎樣可以形成非常簡潔且靈活的代碼:
applyArgument(Lambda::oneStringArgumentMethod, "X");
實現map
在諸多高階函數中,map是最經典的. map的第一個參數是函數,該函數可以接收一個參數并返回一個值;第二個參數是值列表. map使用傳入的函數處理值列表的每一項,然后返回一個新的值列表。下面Python的代碼片段,可以很好的說明map的用法:
>>> map(math.sqrt, [1, 4, 9, 16])[1.0, 2.0, 3.0, 4.0]
在本節的后續內容中,將給出該函數的Java實現。Java 8已經通過Stream提供了該函數。因為主要出于教學目的,所以,本節中給出的實現特意保持簡單,僅限于List對象使用。
與Python不同,在Java中必須首先考慮map第一個參數的類型:一個可以接收一個參數并返回一個值的方法。參數的類型和返回值的類型可以不同。下面接口符合這個預期,顯然,I表示參數(入參),O表示返回值(出參):
interface MapFunction<I, O> {O apply(I in);}
泛型map方法的實現,變得驚人的簡單明了:
static <I, O> List<O> map(MapFunction<I, O> func, List<I> input) {List<O> out = new ArrayList<>();for (I in : input) {out.add(func.apply(in));}return out;}
1.創建新的返回值列表out(用于保存O類型的對象).
2.通過遍歷input,func處理列表的每一項,并將返回值添加到out中。
3.返回out.
下面是實際使用map方法的實例:
MapFunction<Integer, Double> func = Math::sqrt;List<Double> output = map(func, Arrays.asList(1., 4., 9., 16.));System.out.println(output);
在Python one-liner的推動下,可以用更簡潔的方法表達:
System.out.println(map(Math::sqrt, Arrays.asList(1., 4., 9., 16.)));
Java畢竟不是Python...
Lambdas來了!
讀者可能會注意到,還沒有提到Lambdas。這是由于采用了“自下而上”的方式描述,現在基礎已基本建立,Lambdas將在后續的章節中介紹。
下面的用例作為基礎:一個double類型的list,表示半徑,然后得到一個列表,表示圓面積。map方法就是為此任務預先準備的。計算圓面積的公式是眾所周知的:
A?=?r2π
應用這個公式的方法很容易實現:
static Double circleArea(Double radius) {return Math.pow(radius, 2) * Math.PI;}
這個方法現在可以用作map方法的第一個參數:
System.out.println(map(LambdaMap::circleArea,Arrays.asList(1., 4., 9., 16.)));
如果circleArea方法只需要這一次, 沒有道理把類接口被他弄得亂七八糟,也沒有道理將實現和真正使用它的地方分離。最佳實踐是使用用匿名內部類。可以看到,實例化一個實現MapFunction接口的匿名內部類可以很好的完成這個任務:
System.out.println(map(new MapFunction<Double, Double>() {public Double apply(Double radius) {return Math.sqrt(radius) * Math.PI;}},Arrays.asList(1., 2., 3., 4.)));
這看起來很漂亮,但是很多人會認為函數式的解決方案更清晰,更具可讀性:
List<Double> out = new ArrayList<>();for (Double radius : Arrays.asList(1., 2., 3., 4.)) {out.add(Math.sqrt(radius) * Math.PI);}System.out.println(out);
到目前為止,最后是使用Lambda表達式。 讀者應該注意Lambda如何取代上面提到的匿名類:
System.out.println(map(radius -> { return Math.sqrt(radius) * Math.PI; },Arrays.asList(1., 2., 3., 4.)));
這看起來簡潔明了 - 請注意 Lambda 表達式如何缺省任何明確的類型信息。 沒有顯式模板實例化,沒有方法簽名。
Lambda表達式由兩部分組成,這兩部分被->分隔。第一部分是參數列表,第二部分是實際實現。
Lambda表達式和匿名內部類作用完全相同,然而它摒棄了許多編譯器可以自動推斷的樣板代碼。讓我們再次比較這兩種方式,然后分析編譯器為開發人員節省了哪些工作。
MapFunction<Double, Double> functionLambda =radius -> Math.sqrt(radius) * Math.PI;MapFunction<Double, Double> functionClass =new MapFunction<Double, Double>() {public Double apply(Double radius) {return Math.sqrt(radius) * Math.PI;}};
對于Lambda實現來說,只有一個表達式,返回語句和花括號可以省略。這使得代碼更簡短。 Lambda表達式的返回值類型是從Lambda實現推斷出來的。 對于參數類型,我不完全確定,但我認為必須從Lambda表達式所處的上下文中推斷出參數類型。 最后編譯器必須檢查返回值類型是否與Lambda的上下文匹配,以及參數類型是否與Lambda實現匹配。
這一切都可以在編譯期間完成,根本沒有運行時開銷。
“怎么掌握Java中的Lambda”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。