[譯]Kotlin中是應該定義函式還是定義屬性?

極客熊貓發表於2018-05-16

翻譯說明:

原標題: Kotlin: should I define Function or Property?

原文地址: blog.kotlin-academy.com/kotlin-shou…

原文作者: Igor Wojda

最近,我對屬性和函式用法方面感到困惑。這是一個很好的機會去介紹有關Kotlin屬性的概念。那麼問題來了,什麼時候使用函式或者使用屬性呢?我建議你遵循以下最簡單的規則:

  • 屬性是描述狀態
  • 函式是描述行為

讓我們進一步探討他們。屬性代表著一個可以描述物件狀態的資料結構,例如: Person物件可以有name、lastNameweight屬性。

class Person (var name:String, var lastName:String, var weight:Double)
複製程式碼

我們還可以建立一個派生屬性,用於返回fullName,它是由namelastName兩個屬性組合而成的。

class Person (var name:String, var lastName:String, var weight:Double) {
    val fullName = "$name $lastName"
}
複製程式碼

在上述例子中,在Person物件建立期間,屬性值fullName僅僅被賦值一次。有時這是一個理想的行為,但是在這個例子中,當我們去改變了name或者lastName的屬性值時,被儲存在fullName的值不會被更新。幸運的是,Kotlin也提供了使用getter或者setter去自定義屬性訪問器的能力。在下面例子中fullName屬性的值會在每一次被訪問的時候都會被賦值。

class Person (var name:String, var lastName:String, var weight:Double) {
    val fullName
        get() = "$name $lastName"
}
複製程式碼

函式,在另一方面包含了物件所有可以被執行的行為或者動作。我們的Person類可以有 run(),walk()jump() 的方法。

class Person () {
    fun run() { /*doSth*/ }

    fun walk() { /*doSth*/ }

    fun jump() { /*doSth*/ }
}
複製程式碼

重要的是需要注意,該方法可能會有一個間接修改物件狀態的副作用,例如我們每呼叫一次 jump() 方法都會使得person物件中的weight屬性值減少0.1

class Person (var name:String, var lastName:String, var weight:Double) {
    //..

    fun jump() { 
        weight -= 0.1   
        /*doSth*/ 
    }
}
複製程式碼

如果你仍然困惑於什麼時候使用屬性或函式,那麼接下來將會從外部呼叫者的角度去回答這個問題。我們先暫時不管其內部的實現(一個屬性被儲存在哪裡或者行為怎麼實現的)並且需要更加簡單地去看待外部的API(類似於加入第三方的庫到專案中,我們僅僅關注其庫提供API即可)

person.name = "Igor"
person.weight = 79
person.jump()
person.jump()
person.jump()
複製程式碼

使用Kotlin屬性好處之一在於屬性訪問器更加簡潔的語法(我們可以使用height 去替代getHeight()/setHeight()方法),此外就是getter和setter方法更加接近於屬性的宣告,不像Java,我們通常會在一個類的頂部定義成員屬性,然後在其下面定義getter和setter方法。我喜歡屬性委託這個特性,它可以提高複用程式碼的能力。我們可以初始化一個變數僅僅當它被需要使用的時候(lazy delegate) ,無論什麼時候屬性的值發生變化,可以執行一些動作(Observable delegate)或者使用自定義委託實現在另一物件(Android shared preferences, map, browser session, database…) )中簡單地儲存我們屬性.

不錯的是Kotlin允許我們在介面中都可以使用函式和屬性

//BAD
class Person () {
    private var weight:Double = 0

    fun setWeight(weight:Double) {
        this.weight = weight
    }

    fun getWeight(): Double {
        return weight
    }
}

//GOOD
class Person (var weight:Double)
複製程式碼

當我們看到一個以set為字首(如: setHeight())開頭的方法、只有一個引數並且被指定為private的變數或者以get為字首(如: getHeight())開頭的方法、返回一個被指定為private的變數時,我們應該定義一個屬性來替代它。

指南: 如何做這個決定?

每次你想去宣告一個新的函式時,你需要問自己兩個問題:

  • "它是描述一個行為嗎?"-- 將描述行為的函式作為候選者,例如 run(),walk()和jump()
  • "它是描述一個狀態嗎?"-- 將描述狀態的函式作為候選者,例如name,lastName和weight

這裡還有一系列額外來自Effective Java(如果沒記錯的話)的幫助指南讓我們瞭解定義屬性優於函式。

  • 不會丟擲異常
  • 以簡單廉價方式去計算(或者在第一次執行時被快取)
  • 在多個呼叫中返回相同的結果

以上的指南應該可以給你一個不錯的建議,什麼時候去使用函式或者屬性。在一開始,定義屬性或許有點不適應(特別是Java開發者),但是相信我--一段時間後,這個決定是明智的。

譯者有話說:

  • 1、我為什麼要翻譯Kotlin系列部落格?

我們都知道Kotlin這門語言流行不久,目前在國內沒有被大面積應用於實際專案中,而在國外這門語言則非常流行,有很多公司也在大量運用它,在今年的Google IO上,Google官方說他們有35%的工程師喜歡使用Kotlin,所以國外一些Kotlin相關的部落格質量都還不錯。也包括了他們使用Kotlin在一些實際專案中的經驗。一門語言一定得在實際應用中體現它的價值,所以有必要去學習學習。由於國外部落格都是英文的,所以我做了一些翻譯的事情以及我對這邊部落格感受並提煉一些重要點出來。歡迎繼續關注~

  • 2、我為什麼要翻譯這篇部落格?

我們知道學習Kotlin的語法時候與應用於開發是有差距的,在使用過程中會遇到各種選擇的問題,比如這篇部落格是闡述在Kotlin中我們什麼時候去定義一個屬性,什麼時候去定義一個函式呢?這個問題是從基本語法學習中得不到的,得從實際需求和使用經驗中去體會,對於Java轉Kotlin的初學者而言,很容易受到Java語言思想去任意定義函式。因為在Java中是沒有自定義屬性訪問器一說,改變狀態唯有通過函式唄。而在Kotlin提供了一個更為簡單方式去改變屬性狀態。所以當你還在用Java語言思想在使用Kotlin的語言,不妨真正去思考一下使用Kotlin來寫優勢在哪裡?

  • 3、這篇部落格核心點提煉?

關於什麼時候用函式什麼時候用屬性: 描述狀態時,用屬性;描述行為時,用函式

看個例子再次體會下(關於自定義屬性訪問器的知識可以查閱我之前的淺談系列部落格中變數和常量那篇):

Java部分程式碼例子實現

//對於Java而言沒有Kotlin中屬性訪問器之說,對於需要每次訪問時都
//必須保證拿到最新狀態時,Java中唯一好的處理方式就是定義函式,例如isIdling()、isEnded()方法
class VideoPlayer{
    public boolean isIdling() {//描述的是狀態
        return this.mPlayer != null && this.mPlayer.getPlaybackState() == 1;
    }

    public boolean isEnded() {//描述的是狀態
        return this.mPlayer != null && this.mPlayer.getPlaybackState() == 4;
    }
    
    public void play(){//描述的是行為動作
        //do sth    
    }
    
    public void resume(){//描述的是行為動作
        //do sth
    }
}

//呼叫
if (mVideoPlayer.isEnded() || mVideoPlayer.isIdling()) {
    mVideoPlayer.play();         
}else{
    mVideoPlayer.resume();
}
複製程式碼

Kotlin程式碼例子實現

class VideoPlayer{
   var mIsIdling = false//描述的是狀態
       get() = this.mPlayer != null && this.mPlayer.getPlaybackState() == 1
   var mIsEnded = false//描述的是狀態
       get() = this.mPlayer != null && this.mPlayer.getPlaybackState() == 4
    
    fun play(){//描述的是行為動作
        //do sth    
    }
    
    fun resume(){//描述的是行為動作
        //do sth
    }
}

//呼叫
if (mVideoPlayer.mIsEnded || mVideoPlayer.mIsIdling) {
    mVideoPlayer.play()         
}else{
    mVideoPlayer.resume()
}
複製程式碼
[譯]Kotlin中是應該定義函式還是定義屬性?

歡迎關注Kotlin開發者聯盟,這裡有最新Kotlin技術文章,每週會不定期翻譯一篇Kotlin國外技術文章。如果你也喜歡Kotlin,歡迎加入我們~~~

相關文章