函數(shù)式編程——類庫

本文作者:楊龍百炬,叩丁狼高級講師蚕脏。原創(chuàng)文章侦副,轉(zhuǎn)載請注明出處。

基類型和包裝類型

拆箱裝箱的性能問題

將基本類型轉(zhuǎn)換為裝箱類型驼鞭,稱為裝箱秦驯,反之則稱為拆箱,兩者都需要額外的計算開銷挣棕。包裝類型在求和的時候译隘,會出現(xiàn)性能問題。
所以我們會把下面 count 類型聲明基本類型 long洛心。

Long count = 0;
for(int i = 1; i <= 10; i++){
    count += i; // 這會先拆箱后又裝箱
}
System.out.println(count);

函數(shù)編程中如何解決

那么在函數(shù)變成里面若求和是不是會出現(xiàn)上述問題呢固耘?在 Java 8 中,僅對整型词身、長整型和雙浮點型做了特殊處理厅目,因為它們在數(shù)值計算中用得最多,特殊處理后的系統(tǒng)性能提升效果最明顯偿枕。提供下面類型下面這樣函數(shù):

接口 參數(shù) 返回類型
ToLongFunction T long
LongFunction long T
LongUnaryOperator long long
ToIntFunction T int
IntFunction int T
IntUnaryOperator int int
ToDoubleFunction T double
DoubleFunction double T
DoubleUnaryOperator double double
toIntLongDoubleStream.png

Stream 中 map 和 flatMap 的方法的 Lambda 表達式須是以上接口的一個實例璧瞬,這些基本類型都有與之對應(yīng)的 Stream户辫,以基本類型名為前綴渐夸,如 LongStream 。事實上 mapToLong 方法返回的不是一個一般的 Stream渔欢,而是一個特殊處理的 Stream墓塌。這些特殊的 Stream 還提供額外的方法,避免重復(fù)實現(xiàn)一些通用的方法奥额,讓代碼更能體現(xiàn)出數(shù)值計算的意圖苫幢。包含一個 summaryStatistics 方法,這個方法能計算出各種各樣的統(tǒng)計值垫挨,如 IntStream 對象內(nèi)所有元素中的最小值韩肝、最大值、平均值以及數(shù)值總和九榔。

IntStream intStream = Stream.of(1, 2, 3, 4).mapToInt(value -> value);
IntSummaryStatistics intSummaryStatistics = intStream.summaryStatistics();
System.out.println(intSummaryStatistics.getAverage());
System.out.println(intSummaryStatistics.getCount());
System.out.println(intSummaryStatistics.getMax());
System.out.println(intSummaryStatistics.getMin());
/* System.out.println(intStream.average()); 上面那些方法等價下面這些方法
System.out.println(intStream.count());
System.out.println(intStream.max());
System.out.println(intStream.min()); */

重載解析問題

Java 方法中是可以存在重載的哀峻,即同類中方法名相同涡相,方法簽名不相同,但若參數(shù)是一個 Lambda 表達式剩蟀,出現(xiàn)調(diào)用歧義催蝗。

interface IntPredicate {
    public boolean test(int value);
}
class MethodOverloadTest {
    public static void overloadedMethod(Predicate<Integer> predicate) {
        System.out.print("Predicate");
    }
    public static void overloadedMethod(IntPredicate predicate) {
        System.out.print("IntPredicate");
    }
}
public class Main {
    public static void main(String[] args) {
        MethodOverloadTest.overloadedMethod((x) -> true); // 這里編譯會報錯,調(diào)用方法出現(xiàn)歧義
    }
}

最好的解決辦法是不該再重載育特,而應(yīng)當開始重新命名重載方法丙号。

@FunctionalInterface

每個用作函數(shù)接口的接口最好添加這個注解。

該注解會強制 javac 檢查一個接口是否符合函數(shù)接口的標準缰冤。如果該注解添加給一個枚舉類型犬缨、類或另一個注解,或者接口包含不止一個抽象方法棉浸, javac 就會報錯遍尺。重構(gòu)代碼時,使用它能很容易發(fā)現(xiàn)問題涮拗。

接口中默認方法

兼容性問題

雖然 Java 在持續(xù)演進乾戏,但它一直在保持著向后二進制兼容。具體來說三热,使用 Java 1 到 Java 7 編譯的類庫或應(yīng)用鼓择,可以直接在 Java 8 上運行。

Java 8 中為 Collection 接口增加了 stream 方法就漾,這意味著所有實現(xiàn)了 Collection 接口的類都必須增加這個新方法呐能。對核心類庫里的類來說,實現(xiàn)這個新方法抑堡。但在 JDK 之外實現(xiàn) Collection 接口的類摆出,例如 MyCustomList,也仍然需要實現(xiàn)新增的 stream 方法首妖。這就帶來了不兼容的問題偎漫。

解決兼容性問題

在 Java8 中,Collection 接口告訴它所有的子類:“如果你沒有實現(xiàn) stream 方法有缆,就使用我的吧”象踊,接口中這樣的方法叫作默認方法(使用 default 關(guān)鍵字修飾),那么實現(xiàn)類不實現(xiàn)該方法棚壁,也不會編譯報錯杯矩。

Collection {
    default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
    }
}

默認方法的重寫規(guī)則

  • 要優(yōu)先選擇子類中定義方法。
  • 若子類沒有此方法袖外,父類類中重寫的方法優(yōu)先級高于父接口中定義的方法史隆。
interface IWalkable {
    default void walk(){
        System.out.println("IWalkable walk method");
    }
}

class Dog implements IWalkable {
    public void walk(){
        System.out.println("Dog walk method");
    }
}

interface IDanceWalk extends IWalkable{
    default void walk(){
        System.out.println("IDanceWalk walk method");
    }
}

class Husky extends Dog implements IDanceWalk {

}

public class Main {
    public static void main(String[] args) {
        IWalkable walkable = new Dog();     // Dog walk method, 證明第一條重寫規(guī)則
        walkable.walk();
        IWalkable walkable2 = new Husky();  // Dog walk method, 證明第二條重寫規(guī)則
        walkable2.walk();
    }
}

接口多實現(xiàn)繼承問題

當實現(xiàn)類實現(xiàn)多個接口時,多個接口中同時存在相同方法簽名的方法曼验,實現(xiàn)類并沒有覆蓋泌射。

interface A {
    public default String myMehtod() {
        return "A";
    }
}
interface B {
    public default String myMehtod() {
        return "B";
    }
}
// javac 并不明確應(yīng)該繼承哪個接口中的方法头镊,
// 因此編譯器會報錯 C inherits unrelated defaults for myMehtod() from types A and B
class C implements A, B {
}

解決辦法,實現(xiàn)類中覆蓋接口中方法:

class C implements A, B {
    public String myMehtod() {
        return A.super.myMehtod();
    }
}

接口中靜態(tài)方法

Stream 是個接口魄幕,Stream.of 是接口的靜態(tài)方法相艇。這也是 Java 8 中添加的一個新的語言特性,旨在幫助編寫類庫的開發(fā)人員纯陨,但對于日常應(yīng)用程序的開發(fā)人員也同樣適用坛芽。

比如我們自己開發(fā)一個工具類的時候,里面會定義很多靜態(tài)方法翼抠,而這些方法屬性類咙轩,不屬于對象,為了不讓使用者創(chuàng)建該工具類的對象阴颖,一般會把類聲明成私有或者把其構(gòu)造器私有活喊,稍微麻煩一些。

Java8 支持接口中添加靜態(tài)方法量愧,那么以后工具都可以使用接口來定義了更加方便钾菊。

Optional

Optional 是為核心類庫新設(shè)計的一個數(shù)據(jù)類型,用來替換 null 值偎肃。人們常常使用 null 值表示值不存在煞烫,Optional 對象能更好地表達這個概念。Optional 對象相當于值的容器累颂。

使用 null 代表值不存在的最大問題在于 NullPointerException滞详。一旦引用一個存儲 null 值的變量,程序會立即崩潰紊馏。使用 Optional 對象有兩個目的:首先料饥,Optional 對象鼓勵程序員適時檢查變量是否為空,以避免代碼缺陷朱监;其次岸啡,它將一個類的 API 中可能為空的值文檔化,這比閱讀實現(xiàn)代碼要簡單很多赌朋。

Optional API

  • static <T> Optional<T> of(T value) 凰狞,可以從某個值創(chuàng)建出一個 Optional 對象篇裁。
  • static <T> Optional<T> ofNullable(T value)沛慢,可以從某個值(包含 null)創(chuàng)建出一個 Optional 對象。
  • static <T> Optional<T> empty()达布,返回一個沒有值的 Optional 對象团甲。
  • boolean isPresent(),判斷 Optional 對象中是否有值黍聂。
  • T get()躺苦,從 Optional 對象獲取值身腻。
  • T orElse(T other),從 Optional 對象獲取值匹厘,當 Optional 對象為空時嘀趟,該方法提供了一個備選值。
  • T orElseGet(Supplier<? extends T> other)愈诚,從 Optional 對象獲取值她按,當 Optional 對象為空時,該方法提供了一個備選值炕柔,適合備選值計算復(fù)雜時使用酌泰。
  • <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier),從 Optional 對象獲取值匕累,當 Optional 對象為空時陵刹,拋出異常,不設(shè)置 exceptionSupplier 的值拋出的異常是空指針欢嘿,也可以通過 exceptionSupplier 來自定以拋出異常衰琐。
Optional<String> a = Optional.of("a");
assertEquals("a", a.get());
assertTrue(a.isPresent());

Optional emptyOptional = Optional.empty();
Optional alsoEmpty = Optional.ofNullable(null);
assertFalse(emptyOptional.isPresent());
assertFalse(alsoEmpty.isPresent());

assertEquals("b", emptyOptional.orElse("b"));
assertEquals("c", emptyOptional.orElseGet(() -> "c"));

emptyOptional.orElseThrow(null); // 拋出空指針異常
emptyOptional.orElseThrow(() -> new RuntimeException("無值")); // 拋出運行時異常

想獲取更多技術(shù)干貨,請前往叩丁狼官網(wǎng):http://www.wolfcode.cn/all_article.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末炼蹦,一起剝皮案震驚了整個濱河市碘耳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌框弛,老刑警劉巖辛辨,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瑟枫,居然都是意外死亡斗搞,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門慷妙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來僻焚,“玉大人,你說我怎么就攤上這事膝擂÷瞧。” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵架馋,是天一觀的道長狞山。 經(jīng)常有香客問我,道長叉寂,這世上最難降的妖魔是什么萍启? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上勘纯,老公的妹妹穿的比我還像新娘局服。我一直安慰自己,他們只是感情好驳遵,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布淫奔。 她就那樣靜靜地躺著,像睡著了一般堤结。 火紅的嫁衣襯著肌膚如雪搏讶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天霍殴,我揣著相機與錄音媒惕,去河邊找鬼。 笑死来庭,一個胖子當著我的面吹牛妒蔚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播月弛,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼肴盏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了帽衙?” 一聲冷哼從身側(cè)響起菜皂,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎厉萝,沒想到半個月后恍飘,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡谴垫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年章母,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片翩剪。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡乳怎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出前弯,到底是詐尸還是另有隱情蚪缀,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布恕出,位于F島的核電站询枚,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏剃根。R本人自食惡果不足惜哩盲,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望狈醉。 院中可真熱鬧廉油,春花似錦、人聲如沸苗傅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽渣慕。三九已至嘶炭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逊桦,已是汗流浹背眨猎。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留强经,地道東北人睡陪。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像匿情,于是被迫代替她去往敵國和親兰迫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

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