Java8 編程規(guī)范入門之【函數(shù)式接口奈揍、lambda表達(dá)式、方法以及構(gòu)造器引用】

函數(shù)式接口使用背景

我們知道赋续,java是一門面向?qū)ο缶幊陶Z言男翰,java中一切都是面向?qū)ο蟮模ǔ嗽紨?shù)據(jù)類型)。在java中函數(shù)(方法)是類/對象的一部分纽乱,不能單獨(dú)存在蛾绎。而其他一些函數(shù)式編程語言如C++、Javascript等語言鸦列,可以編寫單獨(dú)的函數(shù)并可以直接調(diào)用它們租冠。

面向?qū)ο蟛⒎遣缓茫皇怯袝r(shí)候需要編寫冗長的代碼薯嗤。舉個(gè)簡單的例子顽爹,我們需要創(chuàng)建一個(gè)Runnable實(shí)例,通常我們會使用匿名內(nèi)部類如下:

Runnable r = new Runnable(){
            @Override
            public void run() {
                System.out.println("My Runnable");
            }};

其實(shí)在這段代碼中骆姐,真實(shí)有用的僅僅只是內(nèi)部的run方法镜粤,其他的代碼只是java面向?qū)ο笠蟮摹?/p>

java8函數(shù)式接口和lambda表達(dá)式可以讓我們編寫少量代碼就能達(dá)到上述效果。

java8函數(shù)式接口

在java8中玻褪,本身只有一個(gè)抽象方法的接口即可稱之為函數(shù)式接口肉渴,可以使用@FunctionalInterface注解顯示標(biāo)明接口是函數(shù)式接口。這個(gè)注解并非必須的带射,如果加上該注解黄虱,則接口若存在多于一個(gè)的抽象方法則會提示編譯錯(cuò)誤。

java8函數(shù)式接口的最大好處是可以使用lambda表達(dá)式來初始化函數(shù)式接口從而避免匿名內(nèi)部類樣式的笨重寫法庸诱。

java8的集合API已經(jīng)重寫了捻浦,并且引進(jìn)了使用很多的函數(shù)式接口的新的流式API晤揣。在java.util.function包下定義了很多函數(shù)式接口如:ConsumerSupplier朱灿、Function Predicate昧识。

lambda表達(dá)式

通過lambda表達(dá)式我們可以將函數(shù)式編程在java的面向?qū)ο笾行蜗蠡?br> 對象是java語言的基本,我們不可能離開對象單獨(dú)去使用方法盗扒,這也是為什么java提供lambda表達(dá)式僅僅能使用函數(shù)式接口的原因跪楞。

如果只有一個(gè)抽象方法,那么使用lambda表達(dá)式就不會存在困惑了侣灶。
lambda表達(dá)式的簽名:
*** (argument1, argument2,...) -> (body) ***

  • ** (argument1, argument2,...)**表示方法簽名甸祭,argument1, argument2,...是參數(shù)列表

  • ** -> ** 是箭頭,指向方法體

  • **(body) ** 是方法體褥影,可以使用{}包裹代碼塊來表示

  • 如果是無參方法池户,則方法簽名可以使用 ()

  • 如果只有一個(gè)參數(shù)的話,()可以省略

前面創(chuàng)建Runnable實(shí)例的代碼可以使用lambda表達(dá)式實(shí)現(xiàn):

Runnable r1 = () -> System.out.println("My Runnable");

解釋下這段代碼:

  • Runnable 是一個(gè)函數(shù)式接口凡怎,所以我們可以使用lambda表達(dá)式創(chuàng)建它的實(shí)例

  • 因?yàn)?run()方法咩有參數(shù)校焦,所以我們的lambda表達(dá)式也沒有參數(shù)

  • 就像if-else語句一樣,如果只有一行代碼的話我們可以省去{}符號了统倒。

為什么要使用lambda表達(dá)式

減少代碼量

使用匿名內(nèi)部類和lambda表達(dá)式的代碼量區(qū)分已經(jīng)很明顯了

支持連續(xù)地寨典、并行地執(zhí)行

lambda的另外一個(gè)好處就是我們可以使用流式API連續(xù)并行地執(zhí)行程序。
為了說明這點(diǎn)房匆,我們舉個(gè)例子耸成。判斷一個(gè)數(shù)是不是質(zhì)數(shù):
這段代碼不是最優(yōu)的,但是可以達(dá)到目的:

private static boolean isPrime(int number) {        
    if(number < 2) return false;
    for(int i=2; i<number; i++){
        if(number % i == 0) return false;
    }
    return true;
}

解決這個(gè)問題的代碼是連續(xù)的浴鸿,如果給定的數(shù)字很大的話很耗時(shí)井氢。另外一個(gè)缺陷是分支返回太多可讀性不好。使用lambda和流式API的寫法:

private static boolean isPrime(int number) {        
    return number > 1
            && IntStream.range(2, number).noneMatch(
                    index -> number % index == 0);
}

**IntStream **是一個(gè)自然排好序的元素為原始類型int的支持連續(xù)并行執(zhí)行的流赚楚。為了更好閱讀,代碼可以進(jìn)一步優(yōu)化為:

private static boolean isPrime(int number) {
    IntPredicate isDivisible = index -> number % index == 0;
    
    return number > 1
            && IntStream.range(2, number).noneMatch(
                    isDivisible);
}

range(arg1,arg2)方法返回一個(gè)**IntStream **包含arg1骗卜,但是不包含arg2的步長為1的序列宠页。

noneMatch()返回是否沒有元素匹配不上給定的預(yù)定義條件Predicate。

給方法傳遞行為action

我們需要對一個(gè)list中滿足某個(gè)條件的元素進(jìn)行求和寇仓。

public static int sumWithCondition(List<Integer> numbers, Predicate<Integer> predicate) {
        return numbers.parallelStream()
                .filter(predicate)
                .mapToInt(i -> i)
                .sum();
    }

使用方法如下:

//對所有元素求和
sumWithCondition(numbers, n -> true)
//對是偶數(shù)的元素求和
sumWithCondition(numbers, i -> i%2==0)
//對所有大于5的元素求和
sumWithCondition(numbers, i -> i>5)

高效率的懶加載

如果我們需要找出3-11之間的最大的奇數(shù)举户,并求出它的平方。

我們可能使用這樣的代碼:

private static int findSquareOfMaxOdd(List<Integer> numbers) {
        int max = 0;
        for (int i : numbers) {
            if (i % 2 != 0 && i > 3 && i < 11 && i > max) {
                max = i;
            }
        }
        return max * max;
    }

上述代碼是在一個(gè)序列中處理我們可以使用流式API代替:

public static int findSquareOfMaxOdd(List<Integer> numbers) {
        return numbers.stream()
                .filter(NumberTest::isOdd)  
                .filter(NumberTest::isGreaterThan3)
                .filter(NumberTest::isLessThan11)
                .max(Comparator.naturalOrder())
                .map(i -> i * i)
                .get();
    }

    public static boolean isOdd(int i) {
        return i % 2 != 0;
    }
    
    public static boolean isGreaterThan3(int i){
        return i > 3;
    }
    
    public static boolean isLessThan11(int i){
        return i < 11;
    }

冒號表達(dá)式是方法的引用遍烦,NumberTest::isOdd 是 (i) -> isOdd(i) 或者
i -> NumberTest.isOdd (i) 的縮寫俭嘁。

更多的lambda表達(dá)式示例

() -> {}                     // 無參無方法體

() -> 42                     // 無參有方法體
() -> null                   // 無參有方法體
() -> { return 42; }         // 無參,代碼塊中返回結(jié)果
() -> { System.gc(); }       // 

// 復(fù)雜的代碼塊
() -> {
  if (true) return 10;
  else {
    int result = 15;
    for (int i = 1; i < 10; i++)
      result *= i;
    return result;
  }
}                          

(int x) -> x+1             // 單個(gè)的聲明類型的參數(shù)
(int x) -> { return x+1; } // 
(x) -> x+1                 // 單個(gè)參數(shù)服猪,單條代碼同上
x -> x+1                   // 同上

(String s) -> s.length()   // 
(Thread t) -> { t.start(); } // 
s -> s.length()              // 單個(gè)的編譯器可以推斷的類型參數(shù)
t -> { t.start(); }          // 單個(gè)的編譯器可以推斷的類型參數(shù)

(int x, int y) -> x+y      // 多個(gè)的聲明類型的參數(shù)
(x,y) -> x+y               // 多個(gè)的可以推斷的類型參數(shù)
(x, final y) -> x+y        // 錯(cuò)誤供填。不能修改final變量y
(x, int y) -> x+y          // 錯(cuò)誤拐云,無法推斷混合類型

方法、構(gòu)造器引用

java8可以使用冒號表達(dá)式來引用方法:

System::getProperty
System.out::println
"abc"::length
ArrayList::new
int[]::new
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末近她,一起剝皮案震驚了整個(gè)濱河市叉瘩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌粘捎,老刑警劉巖薇缅,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異攒磨,居然都是意外死亡泳桦,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門娩缰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來灸撰,“玉大人,你說我怎么就攤上這事漆羔∥嗌荩” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵演痒,是天一觀的道長亲轨。 經(jīng)常有香客問我,道長鸟顺,這世上最難降的妖魔是什么惦蚊? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮讯嫂,結(jié)果婚禮上蹦锋,老公的妹妹穿的比我還像新娘。我一直安慰自己欧芽,他們只是感情好莉掂,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著千扔,像睡著了一般憎妙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上曲楚,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天厘唾,我揣著相機(jī)與錄音,去河邊找鬼龙誊。 笑死抚垃,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鹤树,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼铣焊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了魂迄?” 一聲冷哼從身側(cè)響起粗截,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎捣炬,沒想到半個(gè)月后熊昌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡湿酸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年婿屹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片推溃。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡昂利,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出铁坎,到底是詐尸還是另有隱情蜂奸,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布硬萍,位于F島的核電站扩所,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏朴乖。R本人自食惡果不足惜祖屏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望买羞。 院中可真熱鬧袁勺,春花似錦、人聲如沸畜普。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吃挑。三九已至钝荡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間儒鹿,已是汗流浹背化撕。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工几晤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留约炎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像圾浅,于是被迫代替她去往敵國和親掠手。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容