"Effective Java"怎樣影響Kotlin的設計(1)

Java是一門偉大的編程語言喷户,但是它有一些瑕疵,普通的錯誤和從早期版本(1995年發(fā)布的1.0版本)繼承下來的不那么偉大的要素庞钢。有一本廣受尊敬的書專門講解怎么寫好Java代碼拔恰,如何避免常見的編程錯誤以及如何處理Java的這些弱點,那就是Joshua Bloch的大名鼎鼎的"Effective Java"基括。這本書包括78個小節(jié)颜懊,我們將每一個小節(jié)稱為一個“項目”,每一個項目都會在Java語言的不同方面給出非常有價值的建議风皿。

現代編程語言的創(chuàng)建者有一個很大的優(yōu)勢河爹,因為他們可以分析已有語言的弱點然后把事情做得更好。已經開發(fā)了數個流行IDE的 Jetbrains 公司于2010年決定為他們自己的開發(fā)創(chuàng)建一門新的編程語言 Kotlin桐款。該語言的目標就是在消除Java語言的一些弱點的同時更簡潔和更具表達性咸这。他們已有的IDE代碼都是用 Java 開發(fā)的,所以他們需要一種和 Java 高度互操作并且可以編譯成 Java 字節(jié)碼的語言魔眨。他們同時希望 Java 開發(fā)者能夠很容易的轉移到 Kotlin 語言媳维。Jetbrain 希望使用 Kotlin 創(chuàng)建一個更好的 Java。

當再次閱讀 "Effective Java" 時冰沙,我發(fā)現其中的很多“項目”對 Kotlin 已經不適用侨艾,所以本文就是探討一下 “Effective Java”這本書的內容是怎樣影響到 Kotlin 的設計的。

1.有了Kotlin的默認值拓挥,不再需要builders

在Java中如果一個構造函數有很多可選參數時,代碼將變得冗長袋励,不易閱讀并且很容易發(fā)生錯誤侥啤。為了解決這個問題当叭,“Effective Java”的項目2描述了怎樣有效的使用 Builder Pattern。創(chuàng)建這樣一個對象需要很多的代碼盖灸,就像下面的 nutrition facts 對象蚁鳖。它有兩個必須的參數(servingSize, servings)和四個可選的參數(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代碼來創(chuàng)建一個對象時大致就是這樣:

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

在Kotlin中不再需要使用 builder pattern,因為Kotlin具有默認參數這個特性赁炎,默認參數這個特性允許你為每一個可選的構造參數指定默認值:

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)

使用Kotlin來創(chuàng)建一個對象大致就是這樣的:

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

為了獲得更好的閱讀性醉箕,你也可以為必須的兩個參數 servingSizeservings 也指定名稱:

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

像Java一樣,這里創(chuàng)建的這個對象也是不可修改的徙垫。

我們將Java中的47行代碼在Kotlin中縮減成了7行讥裤,這會大大的提高生產效率。

嗯姻报,不錯哦

提示:如果你想在Java中創(chuàng)建 KotlinNutrition 對象己英,你當然可以,但是你必須為每一個可選參數指定一個值吴旋。幸運的是损肛,如果添加了 JvmOverloads 注解,就會生成多個構造函數荣瑟。注意如果想要使用注解治拿,那么也必須添加 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.更容易實現單例

"Effective Java" 的項目3展示了怎樣設計一個 Java 對象使它的行為符合一個單例,就是只能創(chuàng)建一個實例的對象笆焰。下面的代碼片段展示了一個“單例的”世界劫谅,這里只能有一個 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引入了 object declarations的概念,它給我們提供了單例行為:

object KotlinElvis {
    fun leaveTheBuilding() {}
}

不再需要手動構建單例仙辟。

3.自帶equals() 和 hashCode()

源于函數式編程和簡化代碼的一個編程最佳實踐就是盡量使用不可修改的對象同波。項目15包含這樣的建議”所有的類都應該是不可修改的,除非你有非常好的理由使他們成為可修改的“叠国。在Java中創(chuàng)建不可修改的類是非常單調乏味的未檩,因為你必須覆蓋每一個類的equals()hashCode()方法。Joshua Bloch 用了18頁在項目18和項目9中描述了如何遵守這兩個方法的規(guī)則粟焊。例如冤狡,如果你覆蓋了 equals(), 你必須遵守這些規(guī)則:自反性,對稱性项棠,傳遞性和非空性悲雳。聽起來像是數學研究而不是編程。

在Kotlin中香追,你只要簡單的使用 data calss 即可合瓢,編譯器會自動獲得像 equals()hashCode() 等等其他更多的方法透典。這是可能的因為標準的功能可以機械得從對象的屬性中獲取晴楔。這一切只需要簡單的在類名稱前鍵入一個關鍵字 data 顿苇。這里不再需要18頁的描述。

4.屬性(Properties)取代字段(fields)

public class JavaPerson {

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

項目14建議在公共類中使用訪問器方法而不是直接使用公共字段税弃。如果你不遵循這個建議你會陷入麻煩纪岁,因為字段可以直接訪問導致你失去封裝和靈活性所帶來的好處。這意味著以后你如果不修改其公共接口就無法改變其內部表示则果。例如幔翰,以后你不能限制某一個字段的值如年齡。這就是為什么我們總是在 Java 中創(chuàng)建冗長的 getters 和 setters西壮。

在Kotlin中這一最佳實踐已被強制執(zhí)行遗增,因為它使用自帶默認setter 和getter 的properties來取代 fields。

class KotlinPerson {

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

從字面上你可以像Java中的field一樣訪問這些屬性 person.name 或者 person.age 茸时。你可以添加自定義的 getters 和setters 而不需修改這個類的API:

class KotlinPerson {

    var name: String? = null

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

長話短說:使用 Kotlin 的屬性特性贡定,可以獲得更簡潔且更靈活的類。

5.Override是必須的關鍵字而不是一個可選的注解

Java在1.5版本中引入了注解可都。其中最重要的一個就是 Override缓待, 這個注解用于標注一個方法是覆蓋了其父類的一個方法。根據項目36渠牲,Override應該被使用以避免一些惡毒的bug旋炒。當你以為你在覆蓋一個父類的方法,但是實際上并沒有這么做的時候編譯器會拋出一個異常签杈。只要你別忘記 Override注解這就會起效瘫镇。

在Kotlin中,override 不再是一個可選的注解答姥,而是一個必須的關鍵字铣除。所以這些嚴重的bug發(fā)生的機會不大。這些事情編譯器會提前警告你鹦付。Kotlin堅持將這些事情變得顯而易見

本文譯自How “Effective Java” may have influenced the design of Kotlin?—?Part 1

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末尚粘,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子敲长,更是在濱河造成了極大的恐慌郎嫁,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祈噪,死亡現場離奇詭異泽铛,居然都是意外死亡,警方通過查閱死者的電腦和手機辑鲤,發(fā)現死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門盔腔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事铲觉〕候” “怎么了吓坚?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵撵幽,是天一觀的道長。 經常有香客問我礁击,道長盐杂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任哆窿,我火速辦了婚禮链烈,結果婚禮上,老公的妹妹穿的比我還像新娘挚躯。我一直安慰自己强衡,他們只是感情好,可當我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布码荔。 她就那樣靜靜地躺著漩勤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缩搅。 梳的紋絲不亂的頭發(fā)上越败,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天,我揣著相機與錄音硼瓣,去河邊找鬼究飞。 笑死,一個胖子當著我的面吹牛堂鲤,可吹牛的內容都是我干的亿傅。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼瘟栖,長吁一口氣:“原來是場噩夢啊……” “哼葵擎!你這毒婦竟也來了?” 一聲冷哼從身側響起慢宗,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤坪蚁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后镜沽,有當地人在樹林里發(fā)現了一具尸體敏晤,經...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年缅茉,在試婚紗的時候發(fā)現自己被綠了嘴脾。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖译打,靈堂內的尸體忽然破棺而出耗拓,到底是詐尸還是另有隱情,我是刑警寧澤奏司,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布乔询,位于F島的核電站,受9級特大地震影響韵洋,放射性物質發(fā)生泄漏竿刁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一搪缨、第九天 我趴在偏房一處隱蔽的房頂上張望食拜。 院中可真熱鬧,春花似錦副编、人聲如沸负甸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽呻待。三九已至,卻和暖如春短纵,著一層夾襖步出監(jiān)牢的瞬間带污,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工香到, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鱼冀,地道東北人。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓悠就,卻偏偏與公主長得像千绪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子梗脾,可洞房花燭夜當晚...
    茶點故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內容