国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Introducing FP in Java8

Prasanta / 2511人閱讀

摘要:函數(shù)副作用會(huì)給程序設(shè)計(jì)帶來(lái)不必要的麻煩,引入潛在的,并降低程序的可讀性。所以只能采用這種曲線救國(guó)的方式。則是把這種曲線救國(guó)拿到了臺(tái)面上,并昭告天下,同時(shí)還對(duì)提供了一些語(yǔ)法支持。是自由變量,提供執(zhí)行上下文,觸發(fā)閉包執(zhí)行。

背景

自從2013年放棄了Java就再也沒(méi)有碰過(guò)。期間Java還發(fā)布了重大更新:引入lambda,但是那會(huì)兒我已經(jīng)玩了一段時(shí)間Scala,對(duì)Java已經(jīng)瞧不上眼。相比Scala Java 8 的lambda還是too young, too naive!再后來(lái),我有機(jī)會(huì)學(xué)習(xí)了一下C#。雖然C#師承Java,但它已經(jīng)是一門非常現(xiàn)代化的語(yǔ)言了,青出于藍(lán)而勝于藍(lán)。首先是LINQ,提供了一致的,并且非常有表達(dá)力的數(shù)據(jù)操作API。還有像擴(kuò)展函數(shù),匿名類這些語(yǔ)法特性,用起來(lái)也是非常趁手。在技術(shù)層面微軟比甲骨文強(qiáng)了不知道多少倍。而且這幾年Java社區(qū)也有點(diǎn)蕭條,國(guó)內(nèi)再也沒(méi)有出現(xiàn)過(guò)類似JavaEye這樣的高質(zhì)量技術(shù)社區(qū)。種種原因都導(dǎo)致我離Java越來(lái)越遠(yuǎn)。

今年五月份,公司要做新的業(yè)務(wù),團(tuán)隊(duì)里的一些老成員已經(jīng)被動(dòng)態(tài)語(yǔ)言(主要是Python,還有一些Ruby)折磨的無(wú)可奈何,決定重新用回Java。如果不是應(yīng)為這,我恐怕再也不會(huì)用Java了。但是工作嘛,沒(méi)有太多選擇的余地。既然團(tuán)隊(duì)決定了那就硬著頭皮上吧。第一步肯定是要了解一下Java8的函數(shù)式特性(這可是千呼萬(wàn)喚始出來(lái)啊)。這段時(shí)間用下來(lái)的總體感覺(jué)是,沒(méi)我想象中的那么糟,還湊合。下邊總結(jié)了一些Java8函數(shù)式編程的要點(diǎn):包括Optional,lambda表達(dá)式,Stream

Optional

NullPointerException,這個(gè)應(yīng)該是Java里被人詬病最多的了,有的公司應(yīng)為這個(gè)損失的錢可不是小數(shù)。那在Java 8里引入了一個(gè)Optional類型來(lái)解決這個(gè)問(wèn)題。Optional是什么呢?在函數(shù)式編程里Optional其實(shí)就是一個(gè)Monad。和其他編程語(yǔ)言中,比如Scala的Option,Haskell的Maybe異曲同工。

在沒(méi)有Optional之前,通常做法是返回null,或者程序拋出異常,具體使用要看團(tuán)隊(duì)的規(guī)范。但這兩種方式都有各自的問(wèn)題,我再數(shù)落一遍。

對(duì)于返回null,試想如果所有的方法都由可能返回null,那對(duì)方法使用者來(lái)說(shuō)非常恐怖的。處處防,處處防。而且每一個(gè)null都是一個(gè)地雷,哪一個(gè)地方疏忽了都有可能被“炸”到。

拋出異常呢,不會(huì)有處處寫防御代碼的問(wèn)題了。但是這種解決方案也是非常“湊合”。Java中異常分為兩種:受檢異常和非受檢異常。如果使用了非受檢異常程序就會(huì)直接被異常中斷,通常在Spring中是提倡直接拋出非受檢異常的,再搭配上Spring的攔截器,省去了程序員不少麻煩。

受檢異常就不一樣了,使用受檢異常絕不會(huì)比使用null好到哪里去。在方法的簽名上附帶上函數(shù)可能拋出的異常,讓方法使用者去判斷如何處理這個(gè)異常。看起來(lái)這是一種負(fù)責(zé)任的做法,事實(shí)確是把自己不想做的事情(異常處理)交給了方法調(diào)用者。Jackson類庫(kù)就是這樣,每次使用它序列化對(duì)象時(shí)都得考慮是try-catch還是修改方法簽名(告知外部方法處理)。非常不友好。

再有一點(diǎn),拋出Exception意味著這個(gè)函數(shù)(方法)是帶有副作用的。函數(shù)副作用會(huì)給程序設(shè)計(jì)帶來(lái)不必要的麻煩,引入潛在的bug,并降低程序的可讀性。試想一個(gè)函數(shù)從其簽名上來(lái)看應(yīng)該返回一個(gè)訂單,但是結(jié)果卻是它不總能夠返回訂單,有時(shí)還拋出異常。

終于可以開(kāi)始說(shuō)Optional了。用白話形容,Optional就是一個(gè)盒子。當(dāng)你拿到這個(gè)盒子的時(shí)候,盒子可能里邊有想要的東西,但也可能只是一個(gè)空盒子。所以原來(lái)返回null,或者跑出異常的方法我們可以直接返回一個(gè)盒子。來(lái)一個(gè)例子,這個(gè)例子來(lái)改進(jìn)一下Jackson的API:

public Optional json(Object object) {
    String jsonString = null;
    try {
        jsonString = new ObjectMapper().writeValueAsString(object);
    } catch (JsonProcessingException e) {
        e.printStackTrace();
    }

    return Optional.ofNullable(jsonString);
}

這個(gè)新的方法不再讓調(diào)用者處理異常,也不存在返回null的風(fēng)險(xiǎn)。但是問(wèn)題來(lái)了,拿到了這個(gè)“盒子”我們接下來(lái)該怎么辦啊?別著急,Optional提供了非常豐富的接口,幾乎能夠滿足你日常全部的使用場(chǎng)景。以下接口都是非常常用的:

判斷Optional是否有值:op.isPresent()

基于Optional封裝的結(jié)果做一些操作,并繼續(xù)返回一個(gè)Optional類型的結(jié)果:op.map(str -> str.trim())

合并兩個(gè)Optional為一個(gè):op.flatMap(s -> op2.map(s2 -> s1 + s2))

只有在有值的情況下才進(jìn)行一些操作:op.ifPresent(s -> s.trim())

如果Optional有值則返回,沒(méi)有返回給定的默認(rèn)值:op.orElse("hello")
還有一些其它的接口,請(qǐng)自行查閱Optional API文檔。注意在使用Optional的時(shí)候不推薦直接使用get(),因?yàn)檫@樣可能會(huì)拋出異常。

我曾經(jīng)把方法的參數(shù)也置為Optional類型,但是的到了IDEA的警告:Optional不推薦用作方法的參數(shù)。我隨后Google了一些帖子,得到的答案就是:對(duì)于方法的參數(shù),null可能比Optional更好用一些。在Scala和Haskell里是沒(méi)有這樣的限制的,你可以任意的把Option和Maybe當(dāng)做參數(shù)。因?yàn)樵赟cala和Haskell中Option和Maybe是基本的數(shù)據(jù)類型。Java中的Optional則只是一種受限的實(shí)現(xiàn),主要的目的是提供一種清晰,友好的表達(dá)“空”的方式。

Lambda 表達(dá)式

lambda表達(dá)式在上邊我們已經(jīng)用到了,比如op.map(str -> str.trim())str -> str.trim()就是一個(gè)lambda表達(dá)式。Java和其他大多數(shù)支持lambda的語(yǔ)言一樣采用箭頭:-> 來(lái)標(biāo)示lambda。在箭頭的的左邊是0或多個(gè)參數(shù),箭頭的右邊是一個(gè)表達(dá)式或者代碼塊。我們來(lái)看幾個(gè)lambda的實(shí)例:

() -> {}                // No parameters; result is void
() -> 42                // No parameters, expression body
() -> null              // No parameters, expression body
() -> { return 42; }    // No parameters, block body with return
() -> { System.gc(); }  // No parameters, void block body

() -> {                 // Complex block body with returns
  if (true) return 12;
  else {
    int result = 15;
    for (int i = 1; i < 10; i++)
      result *= i;
    return result;
  }
}                          

(int x) -> x+1              // Single declared-type parameter
(int x) -> { return x+1; }  // Single declared-type parameter
(x) -> x+1                  // Single inferred-type parameter
x -> x+1                    // Parentheses optional for
                            // single inferred-type parameter

(String s) -> s.length()      // Single declared-type parameter
(Thread t) -> { t.start(); }  // Single declared-type parameter
s -> s.length()               // Single inferred-type parameter
t -> { t.start(); }           // Single inferred-type parameter

(int x, int y) -> x+y  // Multiple declared-type parameters
(x, y) -> x+y          // Multiple inferred-type parameters
(x, int y) -> x+y    // Illegal: can"t mix inferred and declared types
(x, final y) -> x+y  // Illegal: no modifiers with inferred types

憶苦思甜,我們先來(lái)回憶一下在沒(méi)有l(wèi)ambda之前如果我們想為Optional實(shí)現(xiàn)map功能我們應(yīng)該如何做。通常做法是這樣的:

interface Function {
    public E exec(E e);
}

public static  Optional map(Optional original, Function fn) {
    if(!original.isPresent()) return Optional.empty();
    E e = original.get();
    return Optional.of(fn.exec(e));
}

map(Optional.of(1), new Function() {
    @Override public Integer exec(final Integer i) {
        return i * i;
    }
});

使用一個(gè)接口來(lái)承載一個(gè)函數(shù)。在Java中函數(shù)不是一等公民,是不能用來(lái)傳遞的。所以只能采用這種“曲線救國(guó)”的方式。Java 8則是把這種”曲線救國(guó)”拿到了臺(tái)面上,并昭告天下,同時(shí)還對(duì)lambda提供了一些語(yǔ)法支持。所以上邊我們看到的一些非常簡(jiǎn)短的lambda表達(dá)式,其實(shí)都是一個(gè)interface+一個(gè)抽象方法。

我們可以借助IDE(我使用的是IDEA)把上邊列舉的一些lambda表達(dá)式抽作變量,IDE可以幫助我們自動(dòng)推導(dǎo)類型,我們來(lái)觀察下他們的類型。

Runnable runnable = () -> {};
DoubleSupplier doubleSupplier = () -> 42;
Callable vCallable = () -> null;
IntToDoubleFunction intToDoubleFunction = (int x) -> x + 1;
IntBinaryOperator intBinaryOperator = (int x, int y) -> x + y;

像Callable,Runnable,DoubleSupplier這些接口就是Java內(nèi)置的函數(shù)式接口。Java還提供了非常多的函數(shù)式接口,你可以在java.util.function下找到他們。函數(shù)式接口相比普通的接口有一個(gè)限制:只能有一個(gè)抽象方法。而且Java還提供了一個(gè)注解:@FunctionalInterface。你可以自己聲明新的接口并為它加上這個(gè)注解。

@FunctionalInterface
interface Function {
    public E exec(E e);
}

上邊說(shuō)過(guò)Java 8對(duì)lambda提供了一些額外支持,這種額外的支持就是一些已經(jīng)實(shí)現(xiàn)的方法也能夠用作lambda表達(dá)式。我們看一個(gè)例子:

Optional arg = ...;
arg.ifPresent(System.out::print);

print是PrintStream中已經(jīng)實(shí)現(xiàn)的方法。這種用法相當(dāng)于:x -> System.out.print(x)。對(duì)于類的靜態(tài)方法,類的實(shí)例方法,對(duì)象的實(shí)例方法都可以使用::操作符用在需要傳遞lambda表達(dá)式的地方。

我們接下來(lái)比較一下Java8前后,實(shí)現(xiàn)閉包的異同。先來(lái)看一下閉包的概念。
閉包是指可以包含自由變量的代碼塊。自由變量沒(méi)有在當(dāng)前代碼塊內(nèi)或者任何全局上下文中定義的,而是在定義代碼塊的環(huán)境中定義(執(zhí)行環(huán)境上下文)。所以一次閉包過(guò)程即要執(zhí)行的代碼塊中的自由變量獲得了執(zhí)行環(huán)境上下文中的值。

在Java 8之前閉包可以通過(guò)內(nèi)部類來(lái)實(shí)現(xiàn)。

import java.util.HashMap;
import java.util.Map;

class Cache {
    private Map contents = new HashMap<>();

    class Monitor {
        public Integer size() {
            return Cache.this.contents.size();
        }
    }
}

public class Library {
    public static void main(String[] args) {
        Cache cache = new Cache();
        Cache.Monitor monitor = cache.new Monitor();
        System.out.println("Cache size is: " + monitor.size());
    }
}

contents是自由變量,cache提供執(zhí)行上下文,monitor.size()觸發(fā)閉包執(zhí)行。如果有其他函數(shù)式語(yǔ)言背景的人看到這種方式可能會(huì)感到非常的奇怪,但這就是Java的方式。再來(lái)看一下Java 8中如何實(shí)現(xiàn)一個(gè)閉包。
由于有了lambda表達(dá)式,創(chuàng)建一個(gè)閉包就相當(dāng)簡(jiǎn)潔了:(Integer x) -> x + y。而且這種形式也非常的functional。接著創(chuàng)建一個(gè)執(zhí)行上下文:

int y = 1;
Function add = (Integer x) -> x + y;

add.apply(3); // 4

兩種風(fēng)格迥異,單從語(yǔ)法表達(dá)力來(lái)說(shuō)肯定是lambda更勝一籌。但社區(qū)里也有人擔(dān)心這種簡(jiǎn)潔性會(huì)影響Java的簡(jiǎn)單,造成代碼風(fēng)格不一致,降低代碼可讀性增加維護(hù)成本。仁者見(jiàn)仁智者見(jiàn)智吧,我肯定是支持使用lambda的。代碼風(fēng)格的話團(tuán)隊(duì)最好能有一個(gè)標(biāo)準(zhǔn),不要好東西給用爛了。

Stream

Stream可以說(shuō)是集合處理的殺手锏(就當(dāng)它是殺手锏吧)。我們先拿Stream來(lái)玩一玩:
Stream.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9).map(i -> i * i).reduce(0, (i, r) -> r + i)
這個(gè)例子是計(jì)算0到9的平方和。我們可以想象一下如果用for循環(huán)實(shí)現(xiàn)同樣的邏輯,代碼行數(shù)至少是四五倍。

下面我們對(duì)這個(gè)程序作一個(gè)分解:第一部分是初始化Stream,加載數(shù)據(jù)Stream.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);第二部分是定義數(shù)據(jù)轉(zhuǎn)化:map(i -> i * i),第三部分是聚合結(jié)果:reduce(0, (i, r) -> r + i)。這基本囊括了Stream所有能作以及要作的事情。

創(chuàng)建函數(shù)

Stream類提供了提供了幾個(gè)工廠方法來(lái)構(gòu)建一個(gè)新的Stream。我們先來(lái)看一下of,of用來(lái)構(gòu)建有限個(gè)數(shù)的Stream,比如我們構(gòu)建一個(gè)包含十個(gè)元素的Stream:Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)

Stream還有兩個(gè)方法:generate, iterate,這兩個(gè)函數(shù)都是用來(lái)構(gòu)造無(wú)限流。

generate就收一個(gè)無(wú)參函數(shù):Supplier s,當(dāng)需要一個(gè)常數(shù)或隨機(jī)數(shù)隊(duì)列的時(shí)候可以使用這個(gè)函數(shù)。 Stream.generate(() -> 1)

iterate會(huì)生成一個(gè)迭代的Stream,你需要指定初始值,以及迭代函數(shù)。Stream.iterate(5, x -> x * 10)

除了上邊的幾種方式,我們還能夠方便的將集合轉(zhuǎn)化為Stream,比如List,Set。你只需要在集合變量上.stream()就可以得到一個(gè)Stream。實(shí)際場(chǎng)景中應(yīng)用更多的還是將一個(gè)集合類轉(zhuǎn)化為Stream。

轉(zhuǎn)換函數(shù)

轉(zhuǎn)化函數(shù)我們最常用的是map和filter。Stream也提供了flatMap函數(shù),但是它的使用比較受限,所以原本flatMap的威力大大減弱了。下邊具體解釋一下map和filter函數(shù)。map的工作原理是:為所有的元素應(yīng)用一個(gè)函數(shù)。為了增強(qiáng)理解舉個(gè)現(xiàn)實(shí)生活中的例子:有一簍子蘋果,我們要為它們都貼上標(biāo)簽。map過(guò)程就是拿出每個(gè)蘋果貼上標(biāo)簽然后放到另一個(gè)簍子里。filter就是一個(gè)過(guò)濾器,過(guò)濾出我們想要的東西。比如,我們要從上邊簍子里挑選出大于500克的蘋果。逐個(gè)拿出蘋果稱重,如果大于500克留在簍子中,則放到新簍子中。所以操作數(shù)據(jù)時(shí),直接往這兩個(gè)例子上套用就可以了。

看一個(gè)例子,計(jì)算所有學(xué)生的總分,并取出總分大于450分的學(xué)生:
students.map(s -> calculate(s.getScore())).filter(score -> score > 450);

聚合函數(shù)

聚合函數(shù)主要用來(lái)收集最終的結(jié)果。比如,求出一個(gè)數(shù)字隊(duì)列的總和,求最大最小值,或?qū)⒔Y(jié)果收集到一個(gè)集合中。下邊我們來(lái)操作一個(gè)Integer的Stream,首先求出最大值和最小值:

Stream ints = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 0);

Optional max = ints.max(Integer::compare);
Optional min = ints.min(Integer::compare);

接下來(lái)我們求出這個(gè)數(shù)列的總和,求和我們可以使用reduce函數(shù)。reduce函數(shù)起到一個(gè)匯總的作用。它和hadoop中的reduce,fork/join中的join作用都是一樣的。

Integer sum = ints.reduce(0, (x, y) -> x + y);

我們?yōu)閞educe傳遞了兩個(gè)參數(shù),第一個(gè)是初始值(執(zhí)行累加操作的第一個(gè)值),第二個(gè)是求和lambda。

Java 8還為基本類型提供了相應(yīng)的Stream,比如IntStream,LongStream。使用IntStream我們直接使用sum就可以執(zhí)行求和操作:

IntStream intsStream = ints.mapToInt(i -> i);
intsStream.sum();

下邊我們看一下收集函數(shù):collect()。collect()函數(shù)是將Stream中的元素放到一個(gè)新的容器中。我們上邊的例子中求出了總分大于450分的同學(xué),我們把它放到一個(gè)List中,看一下如何操作:

students.map(s -> calculate(s.getScore())).filter(score -> score > 450).collect(Collectors.toList())

以上Java 8的函數(shù)式特性全部講完,這只是一個(gè)入門講解,不能涵蓋所有的特性及工具類。有興趣的同學(xué),自行探索java.util.stream包。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/66362.html

相關(guān)文章

  • Java 8的Lambda及其在Android 開(kāi)發(fā)中的應(yīng)用

    摘要:由此可以看出,使用可以讓你的代碼在某些情況下達(dá)到何等的簡(jiǎn)潔。如果沒(méi)有參數(shù),那么前面的是必須存在的。我們知道中的,而其實(shí)就是一個(gè)只定義了一個(gè)抽象方法的。也就是說(shuō),可以訪問(wèn)定義它的那個(gè)方法的局部變量。而在里面,還可以訪問(wèn)所謂的局部變量。 上次在盆友圈發(fā)了一張照片 showImg(http://chriszou.com/images/lambda_example.png); 上面的兩段代碼是...

    liuhh 評(píng)論0 收藏0
  • Write Lean Programs

    摘要:使用匿名內(nèi)部類是最經(jīng)典的使用方法之一。可以通過(guò)表達(dá)式替代匿名內(nèi)部類,簡(jiǎn)化設(shè)計(jì)。 OO makes code understandable by encapsulating moving parting, but FP makes code understandable by minimizing moving parts. -Michael Feathers 劉光聰,程序員,敏捷教練,...

    gnehc 評(píng)論0 收藏0
  • Java11的新特性

    摘要:從版本開(kāi)始,不再單獨(dú)發(fā)布或者版本了,有需要的可以自己通過(guò)去定制官方解讀官方細(xì)項(xiàng)解讀穩(wěn)步推進(jìn)系列六的小試牛刀一文讀懂的為何如此高效棄用引擎 Java語(yǔ)言特性系列 Java5的新特性 Java6的新特性 Java7的新特性 Java8的新特性 Java9的新特性 Java10的新特性 Java11的新特性 Java12的新特性 Java13的新特性 序 本文主要講述一下Java11的新...

    April 評(píng)論0 收藏0
  • 【譯】看權(quán)威的wikipedia如何解釋閉包

    摘要:準(zhǔn)確的說(shuō),是形成了的閉包。因此對(duì)象函數(shù)也是對(duì)象和控制流能通過(guò)閉包實(shí)現(xiàn)。這種方式的閉包不再具有引用透明性,即他不再是一個(gè)純函數(shù)。類閉包結(jié)構(gòu)一些語(yǔ)言的特性能夠模擬出閉包的效果。 寫在開(kāi)頭 ?本來(lái)是很討厭談?wù)撻]包這個(gè)話題的,因?yàn)樵谶@一方面我比較傾向于玉伯還有一些朋友的觀點(diǎn),搞懂作用域才是最重要的,單獨(dú)談?wù)撻]包,真的意義不大。 ?今天剛好在wiki上查其他東西的時(shí)候看到了,想了想以前也沒(méi)從比較...

    Alliot 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<