lambdas for_Java 8发布了! — Lambdas教程
lambdas for
為了慶祝幾分鐘前發(fā)布的Java 8的發(fā)布,我正在發(fā)布Java 8 Lambdas教程的草稿版本。 這是學(xué)習(xí)Streams API的一種很好的可視化方式,從第一天開始,它將幫助您開始在自己的應(yīng)用程序中使用lambda。本文計劃在下一期Java Magazine發(fā)行中發(fā)表,因此,請期盼最終的。版本,如果它們在出版截止日期之前,我會盡力納入評論和反饋。
瑪麗有個小拉姆達
自從Java 5中的泛型發(fā)布以來,Java Lambda是進入Java語言最有影響力的功能。它從根本上改變了編程模型,允許開發(fā)一種功能樣式,并支持有效的代碼并行化以利用多核系統(tǒng)。 盡管作為Java開發(fā)人員,您首先會注意到使用Java 8中啟用了lambda的新API獲得的生產(chǎn)力提高。
在本文中,我們將通過使用JavaFX編寫的復(fù)古游戲向您介紹用于處理集合和數(shù)據(jù)的新Streams API。 該游戲既是一個從頭開始編寫的簡單Java 8應(yīng)用程序,用于展示lambda的最佳實踐,又是使用Streams API進行編程的直觀指南。 但是,我們將首先通過介紹lambdas語言更改來奠定基礎(chǔ)。
Lambdas簡介
要使用lambda,必須使用最新的Java SDK(8或更高版本),并在編譯時將語言級別設(shè)置為Java 8。 您可以從以下位置下載最新的Java SDK版本:
- http://www.oracle.com/technetwork/java/javase/downloads/index.html
使用支持新語法的IDE時,開發(fā)lambda變得容易得多。 大多數(shù)Java IDE已更新為具有l(wèi)ambdas支持,并將幫助您進行l(wèi)ambdas的實時錯誤報告和代碼完成。 NetBeans和IntelliJ值得一提,因為它們在Java 8發(fā)行之時即刻提供了最佳的lambda支持,并且都可以與我們在此演示的示例很好地配合使用。
為了演示新的lambdas功能是如何工作的,以下是一小段代碼,它循環(huán)訪問形狀列表并將藍色的形狀更改為紅色:
for (Shape s : shapes) {if (s.getColor() == BLUE)s.setColor(RED); }在Java 8中,您可以使用如下的forEach和lambda表達式來重寫相同的代碼:
shapes.forEach(s -> {if (s.getColor() == BLUE)s.setColor(RED); });lambda表單在Collection接口上使用了一個稱為forEach的新方法,該方法采用lambda表達式并對所有包含的元素求值。 整個Java核心類都進行了類似的API增強,以簡化lambda表達式的使用。
您可能遇到的一個相關(guān)問題是Java團隊如何在不破壞向后兼容性的情況下向接口添加新方法。 例如,如果您具有實現(xiàn)Collection接口的代碼,但未定義forEach方法,那么升級到Java 8不會破壞您的實現(xiàn)嗎? 幸運的是,另一個稱為擴展方法的功能解決了Java 8中的此問題。下面的代碼清單顯示了Collection接口上的forEach實現(xiàn):
interface Collection<T> {default void forEach(Block<T> action) {Objects.requireNonNull(action);for (T t : this)action.apply(t);}// Rest of Collection methods… }注意新的默認關(guān)鍵字,它指示該方法后面將是默認實現(xiàn)。 子類可以自由創(chuàng)建自己的方法實現(xiàn),但是如果未定義,則子類將獲得與接口中定義的相同的標準行為。 這允許將新方法添加到核心Java類以及您自己的庫和項目中的現(xiàn)有接口。
實際的lambda語法非常簡單……以完整的形式在左側(cè)提供類型和參數(shù),在中間加一個破折號,大于號[->],并在其后加上大括號的方法主體:
(int a, int b) -> { return a + b; }在函數(shù)返回值的情況下,可以通過刪除花括號,return關(guān)鍵字和分號來簡化此操作:
(a, b) -> a + b此外,在只有一個參數(shù)的情況下,您可以省略括號:
a -> a * a最后,如果沒有參數(shù),則只需將括號留空,這對于替換Runnable實現(xiàn)或其他無參數(shù)方法很常見:
() -> { System.out.println("done"); }除了基本語法外,還有一種特殊的快捷方式語法稱為“方法引用”,它使您可以快速創(chuàng)建將單個方法引用為實現(xiàn)的lambda表達式。 下表總結(jié)了不同類型的方法引用以及等效的長格式lambda語法。
| 方法參考 | 等效λ | |
| 對象:: toString | obj-> Objects.toString(obj) | 靜態(tài)方法參考 |
| 對象:: toString | obj-> obj.toString() | 會員方法參考 |
| obj :: toString | ()-> obj.toString() | 對象方法參考 |
| 對象::新 | ()->新的Object() | 構(gòu)造方法參考 |
使用新的lambdas方法時,最重要的最后一個概念是創(chuàng)建允許您接受lambda表達式的接口。 為此,具有一個顯式聲明的抽象方法的任何接口都可以用于接受lambda表達式,因此被稱為功能接口。
為了方便起見,他們引入了新的FunctionalInterface批注,可以選擇使用該批注來標記接口,以便在檢查以確保您的接口滿足單個顯式聲明的抽象方法要求時從編譯器獲取幫助:
@FunctionalInterface interface Sum {int add(int a, int b); }這是推薦的最佳實踐,因為它會在功能接口的定義中遇到一些極端情況,例如包含默認方法,這些默認方法使您可以在功能接口上定義多個方法,因為它們不是抽象的,并且不計入單一抽象方法要求。
現(xiàn)在您已經(jīng)對lambda語法有了基本的了解,是時候探索流API并在一個可視示例的上下文中展示lambda的功能了。
Lambdas復(fù)古游戲
瑪麗有點lambda
誰的羊毛潔白如雪
瑪麗去過的任何地方
Lambda一定會去!
如今,視頻游戲都是關(guān)于高分辨率3D圖形,電影品質(zhì)的剪切場景以及從新手到和平主義者的難度級別。 但是,在游戲的美好時光中,我們只有精靈……可愛,像素化的小人物跳舞和RPG穿越精心設(shè)計的瘋狂難度關(guān)卡。
基于Sprite的圖形也很容易編程,從而使我們可以用不到400行代碼構(gòu)建完整的動畫系統(tǒng)。 完整的應(yīng)用程序代碼在GitHub的以下位置:
- https://github.com/steveonjava/ MaryHadALittleLambda
對于游戲中使用的所有圖形,圖像以標準的3×4平鋪格式進行布局,如Mary的相鄰Sprite表中所示。 (當然)使用lambda完成了動畫精靈的代碼,只需在平鋪的圖像周圍移動視口即可生成3幀的行走動畫[水平]并更改角色朝向的方向[垂直]。
ChangeListener<Object> updateImage =(ov, o, o2) -> imageView.setViewport(new Rectangle2D(frame.get() * spriteWidth,direction.get().getOffset() * spriteHeight,spriteWidth, spriteHeight)); direction.addListener(updateImage); frame.addListener(updateImage);為背景添加靜態(tài)圖像,并添加一些關(guān)鍵事件偵聽器以在輸入時移動角色,您便擁有了經(jīng)典RPG游戲的基礎(chǔ)知識!
產(chǎn)生流
有幾種創(chuàng)建新Java 8 Stream的方法。 最簡單的方法是從您選擇的集合開始,然后簡單地調(diào)用stream()或parallelStream()方法來獲取Stream對象,如以下代碼片段所示:
anyCollection.stream();您還可以使用Stream類上的靜態(tài)幫助器方法從一組已知的對象返回流。 例如,要獲取包含一組字符串的流,可以使用以下代碼:
Stream.of("bananas", "oranges", "apples");同樣,您可以使用Stream數(shù)字子類(例如IntStream)取回生成的一系列數(shù)字:
IntStream.range(0, 50)但是,生成新系列最有趣的方法是在Stream類上使用generate和iterate方法。 這些使您可以使用lambda創(chuàng)建新的對象流,該lambda被調(diào)用以返回新對象。 迭代方法特別有趣,因為它將先前創(chuàng)建的對象傳遞給lambda。 這使您可以為每個調(diào)用返回一個不同的對象,例如,迭代地返回彩虹中的所有顏色:
Stream.iterate(Color.RED,c -> Color.hsb(c.getHue() + .1, c.getSaturation(),c.getBrightness()));為了演示它在視覺上是如何工作的,我們將在踩到綿羊的應(yīng)用程序中添加一個新元素。
新的Barn類的代碼如下:
public static class Barn extends MapObject {static final Image BARN = loadImage("images/barn.png");public Barn(Main.Location loc) {super(BARN, loc);}@Overridepublic void visit(Shepherd s) {SpriteView tail = s.getAnimals().isEmpty() ?s : s.getAnimals().get(s.getAnimals().size() - 1);Stream.iterate(tail, SpriteView.Lamb::new).skip(1).limit(7).forEach(s.getAnimals()::add);} }這段代碼指定了用于基于Sprite的圖形的圖像,該圖像被傳遞給超級構(gòu)造函數(shù),并實現(xiàn)了一個visit方法,該方法具有當Mary踏上谷倉時將被執(zhí)行的邏輯。
visit方法中的第一個語句只是從Mary后面的動物列表中獲取最后一個元素,如果還沒有動物,則返回她。 然后將其用作iterate方法的種子,該方法將被傳遞給Lamb構(gòu)造函數(shù)以進行l(wèi)ambda的首次調(diào)用。 然后,由此生成的羔羊?qū)⒈粋鬟f給Lamb構(gòu)造函數(shù)以進行第二次調(diào)用,并且此過程將連續(xù)重復(fù)。
結(jié)果流包括種子,因此我們可以使用skip函數(shù)從流中刪除該種子,并且從理論上講它是無限的。 由于流是惰性的,因此在添加終端操作之前,我們不必擔(dān)心會創(chuàng)建對象,但是固定流長度的一種簡單方法是使用limit函數(shù),我們將參數(shù)7設(shè)置為跟隨瑪麗生出七只羊。 最后一步是添加將使用該流的終端操作。 在這種情況下,我們將使用forEach函數(shù),并將lambda表達式設(shè)置為對動物列表中add方法的方法引用。 執(zhí)行此lambda的結(jié)果是接連跟隨Mary的七個羔羊:
我們要添加到游戲中的下一個元素是彩虹,它將演示Streams API中的過濾。 過濾器函數(shù)的工作方式是采用謂詞lambda,該謂詞對流中的每個元素求值為true或false。 結(jié)果流包含謂詞lambda評估為true的所有元素。
對于彩虹的邏輯,我們將執(zhí)行一個過濾器,該過濾器返回流中每第 4 個動物,并應(yīng)用JavaFX ColorAdjust函數(shù)來改變色相以匹配傳入的顏色。 對于白色,我們使用null(無顏色偏移)。 以下代碼是Rainbow MapObject的visit方法的實現(xiàn):
s.getAnimals().stream().filter(a -> a.getNumber() % 4 == 1).forEach(a -> a.setColor(null)); s.getAnimals().stream().filter(a -> a.getNumber() % 4 == 2).forEach(a -> a.setColor(Color.YELLOW)); s.getAnimals().stream().filter(a -> a.getNumber() % 4 == 3).forEach(a -> a.setColor(Color.CYAN)); s.getAnimals().stream().filter(a -> a.getNumber() % 4 == 0).forEach(a -> a.setColor(Color.GREEN));當瑪麗踩到彩虹時,所有的羔羊都會根據(jù)您指定的顏色值著色:
“ Lamb” da問題1:如果您在參觀彩虹后踏上谷倉會怎樣?
使用過濾的另一種方法是利用添加到Collection API的新方法來接受謂詞lambda。 其中包括removeIf,它過濾掉與給定謂詞不匹配的所有元素,并進行過濾(位于ObservableList上)并返回只包含與謂詞匹配的項的FilteredList。
我們將使用它們來實現(xiàn)Church對象,該對象將過濾“純”動物。 教會職員會煮熟任何白色的動物,以喂養(yǎng)有需要的人。 這包括增加標牌上“已送餐”的計數(shù)器,并從列表中刪除“純”動物。 教會拜訪方法的代碼如下所示。
Predicate<SpriteView> pure =a -> a.getColor() == null;mealsServed.set(mealsServed.get() +s.getAnimals().filtered(pure).size() );s.getAnimals().removeIf(pure);在以下屏幕截圖中,您可以看到連續(xù)踩彩虹和教堂的結(jié)果。
“ Lamb” da問題2:是否可以使用教堂清除所有已經(jīng)著色的動物?
Streams API中最強大的操作可能是地圖功能。 這使您可以將流中的所有元素從一種對象類型轉(zhuǎn)換為另一種對象,并在此過程中執(zhí)行強大的轉(zhuǎn)換。 我們將用它來實現(xiàn)雞舍,所有跟隨瑪麗的動物都將被轉(zhuǎn)換成卵。
對于雞舍,我有兩種訪問方法的實現(xiàn)。 第一個使用帶有l(wèi)ambda表達式的單個map操作用雞蛋替換流元素,如下所示:
// single map: s.getAnimals().setAll(s.getAnimals().stream().map(sv -> new Eggs(sv.getFollowing()) ).collect(Collectors.toList()));第二種實現(xiàn)使用方法引用和一組映射操作鏈來首先將流轉(zhuǎn)換為動物所跟隨的流,然后調(diào)用構(gòu)造函數(shù)方法引用來創(chuàng)建雞蛋,并將以下信息傳遞給構(gòu)造函數(shù)參數(shù):
// or a double map: s.getAnimals().setAll(s.getAnimals().stream().parallel().map(SpriteView::getFollowing).map(Eggs::new).collect(Collectors.toList()) );這兩個代碼片段的行為和執(zhí)行相似,因為流API被設(shè)計為惰性的,并且僅在調(diào)用終端操作(例如collect)時評估流。 因此,它主要是您更喜歡使用的樣式問題。 使用新的雞舍MapObject運行該程序,可以讓您從羔羊生成雞蛋,如下圖所示:
問題3:如果您將彩色的羊羔送到雞舍,雞蛋是什么顏色?
請注意,每個雞蛋精靈包含三個小彈跳雞蛋。 如果我們能把這些家伙孵化成雞,那不是很好嗎?
要孵化這些雞蛋,我們將為巢添加一個新的MapObject,其中使用以下孵化方法將這些雞蛋孵化成三只雞的組:
public static Stream<SpriteView> hatch(SpriteView sv) {if (!(sv instanceof Eggs)) {return Stream.of(sv);}return Stream.iterate(sv, Chicken::new).skip(1).limit(3); }請注意,此方法返回一個對象流,這意味著如果我們使用常規(guī)映射操作,我們將返回一個流流。 為了將Stream扁平化為單個雞列表,我們可以改用flatMap,它將既使用lambda函數(shù)映射流,又將嵌套的Stream折疊為單個對象列表。 使用flatMap的嵌套訪問功能的實現(xiàn)如下所示:
s.getAnimals().setAll(s.getAnimals().stream().parallel().flatMap(SpriteView.Eggs::hatch).collect(Collectors.toList()) );現(xiàn)在,將雞蛋放到巢中后,您將得到雞的爆炸,如以下屏幕截圖所示:
問題4:在游戲內(nèi)存不足之前,您可以添加大約幾只動物?
我們將添加的最后一個元素是一只狐貍,以演示如何減少流。 為此,我們將首先根據(jù)動物的規(guī)模將流映射到整數(shù)列表,然后使用sum方法引用將其減少為單個值。 reduce函數(shù)需要一個種子值(我們將使用0作為其值),以及一個可以將兩個元素簡化為單個結(jié)果的函數(shù)。 該lambda將遞歸應(yīng)用于流中的所有元素,直到得到單個值,該值將是所有動物比例的總和。
Double mealSize = shepherd.getAnimals().stream().map(SpriteView::getScaleX).reduce(0.0, Double::sum);setScaleX(getScaleX() + mealSize * .2); setScaleY(getScaleY() + mealSize * .2); shepherd.getAnimals().clear();然后,我們將總和(存儲到名為mealSize的變量中)并使用該總和按比例拉伸狐貍。 您可以在下圖中看到為狐貍吃一頓美味的食物的結(jié)果:
問題5:您如何更改Fox的代碼以使其在進食時變胖?
在本文中,我們介紹了基本的lambda語法,包括方法引用,擴展方法和功能接口。 然后,我們在Streams API中進行了詳細介紹,展示了一些常見的操作,例如迭代,過濾,映射,flatMap和reduce。 如您所見,Java 8 lambda極大地改變了編程模型,使您可以編寫更簡單,更精美的代碼,并為諸如Streams之類的新的強大API提供了可能性。 現(xiàn)在是時候在您自己的開發(fā)中開始利用這些功能了。
翻譯自: https://www.javacodegeeks.com/2014/03/java-8-released-lambdas-tutorial.html
lambdas for
總結(jié)
以上是生活随笔為你收集整理的lambdas for_Java 8发布了! — Lambdas教程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ddos防御工具(防DDOS的工具)
- 下一篇: 诈骗备案流程图(诈骗备案流程)