Kotlin學習筆記(6)- 屬性

系列文章全部為本人的學習筆記,若有任何不妥之處,隨時歡迎拍磚指正。如果你覺得我的文章對你有用,歡迎關注我,我們一起學習進步!
Kotlin學習筆記(1)- 環境配置
Kotlin學習筆記(2)- 空安全
Kotlin學習筆記(3)- 語法
Kotlin學習筆記(4)- 流程控制
Kotlin學習筆記(5)- 類
Kotlin學習筆記(6)- 屬性
Kotlin學習筆記(7)- 接口
Kotlin學習筆記(8)- 擴展
Kotlin學習筆記(8)- 擴展(續)
Kotlin學習筆記(9)- 數據類
Kotlin學習筆記(10)- 泛型
Kotlin學習筆記(11)- 內部類和嵌套類
Kotlin學習筆記(12)- 委托
Kotlin學習筆記(13)- 函數式編程
Kotlin學習筆記(14)- lambda

一、屬性聲明

一般的說,類是屬性和邏輯的集合。我們用方法封裝和處理邏輯,用變量聲明屬性。所以可以大言不慚的說一句,屬性聲明在類中是必不可少的。在《Kotlin學習筆記(3)- 語法》中我們介紹過,kotlin中的屬性聲明有兩種:var聲明普通變量,val聲明只讀變量(暨final類型)。其實還有幾點需要說明。

  1. 類中聲明的屬性,一定要初始化,否則會編譯錯誤。除非你對屬性使用了abstract進行修飾。

    var name: String = ""
    abstract var size : Int
    
  2. 屬性聲明中的屬性類型在大部分情況下是可以省略的。這里說的大部分是指我們的最多的使用情況,也就是默認的使用場景。

    var name = ""
    

    那什么時候是不能省略的呢?這還要說到另一個問題,那就是屬性的修飾符,和方法類似的,屬性也有多種修飾符。

    public  // 默認的修飾符。全部可見,在屬性被初始化時,
            // 如果可以根據屬性的值推斷出屬性類型,則可省略類型
    protected // 在本身和子類中可見,如果可以根據屬性的值推斷出屬性類型,則可省略類型
    private  // 只在本身可見,如果可以根據屬性的值推斷出屬性類型,則可省略類型
    abstract  // 在自身不初始化,需要子類進行初始化,不能省略類型。
    
  3. 類中的屬性,用.進行訪問例如上面的name屬性,我們可以這樣進行讀寫操作

    var person = Person()
    person.name = "jck"      // 寫
    Log.d("log", person.name)  // 讀
    

二、getter和setter

  1. 默認方法

    上面的例子可以看到,我們可以直接調用person.name對屬性進行讀寫操作,這種操作我們在java中也見過,是對類中的public屬性進行操作。而在kotlin中,name屬性雖然也是public的,但是意義和java中是完全不同的,這里的讀寫其實是對getset函數的隱式調用,而getset函數是默認實現的,而顯式的寫出來則是這樣

    // 非源碼,而是根據自己理解寫出的。這里的field下面會說
    var name: String = ""
        get() = field            // get
        set(value) {         // set
            field = value
        }
    

    也就是說,之前我們說到的person.name的讀寫操作,其實是對getset方法的訪問,而并不是像java中的直接對屬性的操作,保證了屬性的閉合性。完整的聲明如下:

    var <propertyName>: <PropertyType> [= <property_initializer>]
    [<getter>]
    [<setter>]
    

    其中initializer, getter 和 setter都是可選的。var是允許有getter 和 setter方法,val不允許有setter方法。如果屬性值的數據類型可以通過編譯器自動推斷,或者在getter和setter方法中并沒有對屬性做特殊處理,這些方法都可以省略。

  2. 訪問權限

    在java中我們可以根據需要決定是否實現屬性的getset方法,kotlin中自然也有針對針對這種需求的實現。首先屬性都是有getset方法的,當我們不想對外公開某個方法時,可以使用修飾符private實現,例如

    var name: String = ""
     private set
    

    但是這種方式只適用于set方法,get的訪問權限默認是和屬性一致的,下面的使用會報編譯錯誤

    var name: String = ""
     private get             // 編譯錯誤,get的訪問權限和屬性一致
    

    如果你們有一個需求,要求對某個屬性只可寫,不可讀,那么請用方法fun實現吧……

  3. 自定義getter和setter

    上面說了getset的默認實現,那么就再來說說自定義實現,當然,像上面說過的,varsetget,val只有get

    // 自定義get
    var size: Int = 2
        get() = if (field > 10) 15 else 0
    
    // 調用
    var pf = PropertiesFields()
    pf.size = 5
    Log.d("text", "size : ${pf.size}")
    pf.size = 20
    Log.d("text", "size : ${pf.size}")
    
    // 輸出
    size : 0
    size : 15
    
    // 自定義set
    var size: Int = 2
        set(value) {
            field = if (value > 10) 15 else 0
        }
    
    // 調用和輸出同上
    

    上面的代碼很簡單,就是一個if表達式 ,就不多解釋了。其實kotlin中的gettersetter理解起來很簡單,就像java中的所有屬性都是private并且實現了gettersetter,其他的像權限和自定義問題,都和java中類似。

三、后端變量(Backing Fields)

看大神是這么翻譯Backint Fields的,那我們也這么叫好了。這里就要說到上面提到的field了,在kotlin的gettersetter是不允許本身的局部變量的,因為屬性的調用也是對get的調用,因此會產生遞歸,造成內存溢出。

var count = 1
var size: Int = 2
set(value) {
    Log.e("text", "count : ${count++}")
    size = if (value > 10) 15 else 0
}
內存溢出.png

kotlin為此提供了一種我們要說的后端變量,也就是field。編譯器會檢查函數體,如果使用到了它,就會生成一個后端變量,否則就不會生成。我們在使用的時候,用field代替屬性本身進行操作。

var size: Int = 2
val isEmpty: Boolean
    get() = this.size == 0

這是官方文檔的一個例子,在訪問屬性值isEmpty時,并不會生成后端變量。

這里我有一個疑惑。我們說過類屬性是一定要初始化的,但是我在編譯這個例子的時候確實是沒問題的。我又嘗試著將isEmpty改為普通變量,然后就編譯出錯,希望朋友能幫我解惑,謝謝。

var size: Int = 2
var isEmpty: Boolean          // 這樣就編譯報錯
    get() = this.size == 0

四、后端屬性

如果上面的方案都不符合你的需求,那么可以試試“后端屬性”(backing property)的方法,它實際上也是隱含試的對屬性值的初始化聲明,避免了空指針。

private var _table: Map<String, Int>? = null
val table: Map<String, Int>
    get() {
        if (_table == null)
            _table = HashMap() // Type parameters are inferred
        return _table ?: throw AssertionError("Set to null by another thread")
    }

從各種角度看,這和在Java中定義Bean屬性的方式一樣。因為訪問私有的屬性的gettersetter函數,會被編譯器優化成直接反問其實際字段。

五、編譯器常數值

如果在編譯期間,屬性值就能被確定,該類屬性值使用const 修飾符,將屬性標記為編譯期常數值(compile timeconstants). 這類屬性必須滿足以下所有條件:

  • 必須是頂級屬性,或者是一個object的成員
  • 值被初始化為 String 類型,或基本類型(primitive type)
  • 不存在自定義的取值方法

六、延遲初始化屬性(lateinit)

我們說過,在類內聲明的屬性必須初始化,如果設置非NULL的屬性,應該將此屬性在構造器內進行初始化。假如想在類內聲明一個NULL屬性,在需要時再進行初始化(最典型的就是懶漢式單例模式),與Kotlin的規則是相背的,此時我們可以聲明一個屬性并延遲其初始化,此屬性用lateinit修飾符修飾。

// 延遲初始化聲明
lateinit var late : String
fun initLate(){
    late = "I am late"
}

// 先調用方法,再調用屬性
var pf = PropertiesFields()
pf.initLate()
Log.d("text", pf.late)

// 輸出
I am late

需要注意的是,我們在使用的時候,一定要確保屬性是被初始化過的,通常先調用初始化方法,否則會有異常。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評論 6 538
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,278評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,241評論 2 375

推薦閱讀更多精彩內容