“Effective Java” 可能對(duì) Kotlin 的設(shè)計(jì)造成了怎樣的影響——第一部分

簡(jiǎn)評(píng):作者從《Effective Java》中選出了幾條項(xiàng)目午绳,舉例分析 Java 和 Kotlin 寫(xiě)法不同之處,Kotlin 從中得到啟發(fā)拄丰,寫(xiě)法更加簡(jiǎn)潔减牺。

Java 是一種很好的語(yǔ)言但是有些眾所周知的瑕疵,常見(jiàn)的陷阱以及早期(1.0 在 1995 年發(fā)布)繼承過(guò)來(lái)的不是很好的元素苏潜。一本備受推崇的關(guān)于如何寫(xiě)好 Java 代碼的書(shū),避免常見(jiàn)的編碼失誤变勇,處理它的弱點(diǎn)的書(shū)就是 Joshua Bloch 的《Effective Java》恤左。它包含 78 個(gè)部分,為讀者提供有關(guān)語(yǔ)言不同方面的寶貴意見(jiàn)搀绣。

現(xiàn)代編程語(yǔ)言的創(chuàng)造者有個(gè)很大的優(yōu)勢(shì)飞袋,因?yàn)樗鼈兛梢苑治鲆呀?jīng)創(chuàng)立的語(yǔ)言的弱點(diǎn),從而改進(jìn)它們链患。Jetbrains巧鸭,創(chuàng)建了幾款非常流行的 IDE 的公司,在 2010 年決定創(chuàng)建 Kotlin 編程語(yǔ)言作為自己的開(kāi)發(fā)語(yǔ)言麻捻。它的目標(biāo)是更簡(jiǎn)潔和更清晰的表達(dá)纲仍,同時(shí)消除Java的一些弱點(diǎn)。他們之前寫(xiě) IDE 的代碼都是用 Java 寫(xiě)的贸毕,所以他們需要一種與 Java 高度互操作的語(yǔ)言郑叠,并編譯成Java 字節(jié)碼。他們也想要 Java 開(kāi)發(fā)者非常容易上手 Kotlin明棍。使用 Kotlin乡革,Jetbrains 想要構(gòu)建更好的 Java。

1. 使用 Kotlin 的默認(rèn)值,不再需要 builder 模式

當(dāng)你在 Java 的構(gòu)造器中有很多可選的參數(shù)時(shí)沸版,代碼就會(huì)變得冗長(zhǎng)嘁傀,難讀而且容易出錯(cuò)。為了避免這種情況视粮,《Effective Java》的第二條項(xiàng)目描述了如何高效使用 Builder 模式细办。構(gòu)建這樣的對(duì)象需要很多代碼,比如下面的營(yíng)養(yǎng)成分對(duì)象代碼示例馒铃。它需要兩個(gè)必需參數(shù)(servingSize, servings) 以及四個(gè)可選參數(shù) ( calories, fat, sodium, carbohydrates):

public class JavaNutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    public static class Builder {
        // Required parameters
        private final int servingSize;
        private final int servings;

        // Optional parameters - initialized to default values
        private int calories      = 0;
        private int fat           = 0;
        private int carbohydrate  = 0;
        private int sodium        = 0;

        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings    = servings;
        }

        public Builder calories(int val)
        { calories = val;      return this; }
        public Builder fat(int val)
        { fat = val;           return this; }
        public Builder carbohydrate(int val)
        { carbohydrate = val;  return this; }
        public Builder sodium(int val)
        { sodium = val;        return this; }

        public JavaNutritionFacts build() {
            return new JavaNutritionFacts(this);
        }
    }

    private JavaNutritionFacts(Builder builder) {
        servingSize  = builder.servingSize;
        servings     = builder.servings;
        calories     = builder.calories;
        fat          = builder.fat;
        sodium       = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}

使用 Java 代碼來(lái)實(shí)例化對(duì)象看起來(lái)像這樣:

final JavaNutritionFacts cocaCola = new JavaNutritionFacts.Builder(240,8)
    .calories(100)
    .sodium(35)
    .carbohydrate(27)
    .build();

使用 Kotlin 你根本不需要使用 Builder 模式蟹腾,因?yàn)樗心J(rèn)參數(shù)的功能,允許你定義每個(gè)可選構(gòu)造器參數(shù)的默認(rèn)值区宇。

class KotlinNutritionFacts(
        private val servingSize: Int,
        private val servings: Int,
        private val calories: Int = 0,
        private val fat: Int = 0,
        private val sodium: Int = 0,
        private val carbohydrates: Int = 0)

創(chuàng)建一個(gè) Kotlin 對(duì)象看起來(lái)像這樣:

val cocaCola = KotlinNutritionFacts(240,8,
                calories = 100,
                sodium = 35,
                carbohydrates = 27)

為了更好的可讀性娃殖,你也可以聲明必需的參數(shù) servingSize 和 servings:

val cocaCola = KotlinNutritionFacts(
                servingSize = 240,
                servings = 8,
                calories = 100,
                sodium = 35,
                carbohydrates = 27)

像 Java 一樣,這里對(duì)象的創(chuàng)建是不可改變的议谷。
我們將 Java 所需的 47 行代碼減少到 Kotlin 的 7 行炉爆,生產(chǎn)率有很大的提高。


提示:如果你想要在 Java 中創(chuàng)建 KotlinNutrition 對(duì)象卧晓,你當(dāng)然可以那樣做芬首,但是你被迫為每個(gè)可選參數(shù)指定一個(gè)值。幸運(yùn)的是逼裆,如果你加上 JvmOverloads 注解郁稍,多種構(gòu)造器就被生成了。注意如果你想要使用一個(gè)注解胜宇,我們需要聲明關(guān)鍵詞 constructor :

class KotlinNutritionFacts @JvmOverloads constructor(
        private val servingSize: Int,
        private val servings: Int,
        private val calories: Int = 0,
        private val fat: Int = 0,
        private val sodium: Int = 0,
        private val carbohydrates: Int = 0)

2. 輕松創(chuàng)建單例

《Effective Java》的第三條建議展示了將一個(gè) Java 對(duì)象設(shè)計(jì)成單例耀怜,意味著只有一個(gè)實(shí)例能被實(shí)例化。下面的代碼片段顯示了 “ monoelvistic” 宇宙桐愉,只有一個(gè) Elvis 存在:

public class JavaElvis {

    private static JavaElvis instance;

    private JavaElvis() {}

    public static JavaElvis getInstance() {
        if (instance == null) {
            instance = new JavaElvis();
        }
        return instance;
    }

    public void leaveTheBuilding() {
    }
}

Kotlin 有對(duì)象聲明的概念财破,給了我們一個(gè)單例行為,開(kāi)箱即用:

object KotlinElvis {

    fun leaveTheBuilding() {}
}
view raw

再也不需要手動(dòng)構(gòu)建單例模式了从诲!

3. equals() 和 hashCode() 開(kāi)箱即用

一個(gè)良好的編程實(shí)踐起源于函數(shù)編程左痢,簡(jiǎn)化代碼主要是使用不可變值對(duì)象。第 15 項(xiàng)就是“類應(yīng)該是不可變的除非有一個(gè)非常好的理由讓它們可變系洛】⌒裕”不可變值對(duì)象在 Java 中的創(chuàng)建非常乏味,因?yàn)槊恳粋€(gè)對(duì)象你都要重寫(xiě) equals() 和 hashCode() 方法描扯。這在第 8 和第 9 項(xiàng)中花費(fèi)了 Joshua Bloch 18 頁(yè)來(lái)描述如何遵循這一詳盡的普遍的準(zhǔn)則磅废。例如,如果你重寫(xiě) equals()荆烈,則必須確保靈活性拯勉,對(duì)稱性竟趾,傳遞性,一致性和無(wú)效性的合同都得到滿足宫峦。聽(tīng)起來(lái)更像數(shù)學(xué)而不是編程岔帽。

在 Kotlin 中,你只要使用 data 類即可导绷。編譯器自動(dòng)導(dǎo)出像 equals() 和 hashCode() 等方法犀勒。這是可能的,因?yàn)闃?biāo)準(zhǔn)函數(shù)可以從對(duì)象的屬性機(jī)械地派生妥曲。只要在你的類前面輸入關(guān)鍵字 data 即可贾费。不需要 18 頁(yè)的描述了。

提示:最近 AutoValue 在 Java 中變得很流行檐盟,這個(gè)庫(kù)為 Java 1.6+ 生成不可變值類褂萧。

4. 屬性替代字段

public class JavaPerson {

    // don't use public fields in public classes!
    public String name;
    public Integer age;
}

第 14 項(xiàng)建議在公共類中使用訪問(wèn)器方法而不是公共字段。如果你不遵循這個(gè)建議可能會(huì)有麻煩葵萎,因?yàn)槿绻侄慰梢灾苯釉L問(wèn)那么就喪失了封裝和靈活性帶來(lái)的好處导犹。這意味著將來(lái)你不更改其公共 API 將無(wú)法改變你的類的內(nèi)部表示。例如羡忘,你無(wú)法限制字段的值谎痢,如某人的年齡等等。這是一個(gè)為什么我們總是創(chuàng)建冗長(zhǎng)的 getter 和 setter 方法的原因卷雕。

最佳實(shí)踐被 Kotlin 增強(qiáng)了节猿,因?yàn)樗褂米詣?dòng)生成的默認(rèn) getter 和 setter 方法的屬性替代了字段。

class KotlinPerson {

    var name: String? = null
    var age: Int? = null
}

在語(yǔ)法上漫雕,你可以像使用 person.name 或者 person.age 訪問(wèn) Java 的公共字段一樣訪問(wèn)這些屬性沐批。你也可以自定義 getter 和 setter 方法而不需要改變類的 API:

class KotlinPerson {

    var name: String? = null

    var age: Int? = null
    set(value) {
        if (value in 0..120){
            field = value
        } else{
            throw IllegalArgumentException()
        }
    }
}

長(zhǎng)話短說(shuō):使用 Kotlin 的屬性,我們的類更加簡(jiǎn)潔也更加靈活蝎亚。

5. Override 變成了強(qiáng)制性的關(guān)鍵字而不是可選的注解

注解在 Java 發(fā)布的 1.5 版本中增加了。最重要的一個(gè)就是 Override先馆,標(biāo)記著一個(gè)方法正在重寫(xiě)父類的方法发框。根據(jù)第 36 項(xiàng),為了避免歹毒的 bug煤墙,可選的注解應(yīng)該不斷地被使用梅惯。當(dāng)你認(rèn)為你正在重寫(xiě)父類的方法而實(shí)際上沒(méi)有時(shí),編譯器會(huì)拋出一個(gè)錯(cuò)誤仿野。只要當(dāng)你沒(méi)有忘記使用 Override 注解铣减,那么就沒(méi)有問(wèn)題。

在 Kotlin 中脚作,override 不再是可選的注解而是強(qiáng)制的關(guān)鍵字葫哗。所以像這類難受的 bug 不會(huì)再出現(xiàn)缔刹,編譯器將會(huì)提前警告你。Kotlin 堅(jiān)持把這些事弄清楚劣针。

原文鏈接:How “Effective Java” may have influenced the design of Kotlin?—?Part 1

延伸閱讀:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末襟己,一起剝皮案震驚了整個(gè)濱河市引谜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌擎浴,老刑警劉巖员咽,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異退客,居然都是意外死亡骏融,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門(mén)萌狂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)档玻,“玉大人,你說(shuō)我怎么就攤上這事茫藏∥笈浚” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵务傲,是天一觀的道長(zhǎng)凉当。 經(jīng)常有香客問(wèn)我,道長(zhǎng)售葡,這世上最難降的妖魔是什么看杭? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮挟伙,結(jié)果婚禮上楼雹,老公的妹妹穿的比我還像新娘。我一直安慰自己尖阔,他們只是感情好贮缅,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著介却,像睡著了一般谴供。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上齿坷,一...
    開(kāi)封第一講書(shū)人閱讀 49,730評(píng)論 1 289
  • 那天桂肌,我揣著相機(jī)與錄音数焊,去河邊找鬼。 笑死轴或,一個(gè)胖子當(dāng)著我的面吹牛昌跌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播照雁,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蚕愤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了饺蚊?” 一聲冷哼從身側(cè)響起萍诱,我...
    開(kāi)封第一講書(shū)人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎污呼,沒(méi)想到半個(gè)月后裕坊,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡燕酷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年籍凝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片苗缩。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡饵蒂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出酱讶,到底是詐尸還是另有隱情退盯,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布泻肯,位于F島的核電站渊迁,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏灶挟。R本人自食惡果不足惜琉朽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望稚铣。 院中可真熱鬧箱叁,春花似錦、人聲如沸榛泛。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)曹锨。三九已至,卻和暖如春剃允,著一層夾襖步出監(jiān)牢的瞬間沛简,已是汗流浹背齐鲤。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留椒楣,地道東北人给郊。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像捧灰,于是被迫代替她去往敵國(guó)和親淆九。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

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