Kotlin入門-必會,屬性篇

在這裏插入圖片描述

前言

本文是基礎,需要通篇看。

除了文字版本,也有Xmind版本 github地址

同樣也帶着幾個問題來看:
① 幕後字段與幕後屬性的區別
② val var什麼區別
③ field字段什麼時候不會有
④ 屬性延遲初始化怎麼實現?
⑤ 什麼是編譯期常量

本文目錄如下

  • 定義
  • getter和setter
    • var
    • val
    • getter和setter方法的權限
    • 注意
  • 幕後字段
    • 什麼時候沒有field字段?
  • 幕後屬性
    • 定義
    • 對於 JVM 平臺
  • 幕後字段與幕後屬性的區別
  • 編譯期常量
  • 延遲初始化屬性與變量
    • 解決辦法
    • 適用範圍
    • 注意
    • 檢測一個 lateinit var 是否已初始化(自 1.2 起)
  • 覆蓋屬性
  • 委託屬性

定義

val var是不是有一股JS的味道。
屬性定義有兩種

  • var 可變
  • val 不可變

屬性引用
myInstance.myVar


getter和setter

格式是這樣的

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

getter 和 setter 都是可選
有幾個規則。

  • val是不可變,so沒有setter

var

可變的可賦值的變量

var allByDefault: Int? // 錯誤: 需要一個初始化語句, 默認實現了 getter 和 setter 方法
var initialized = 1    // 類型爲 Int, 默認實現了 getter 和 setter

val

不可變的不可賦值的變量

val simple: Int?       // 類型爲 Int ,默認實現 getter ,但必須在構造函數中初始化
val inferredType = 1   // 類型爲 Int 類型,默認實現 getter

範例一: 修改了get方法

var lastName: String = "zhang"
get() = field.toUpperCase()   // 將變量賦值後轉換爲大寫
set

範例二: 修改了set方法

var no: Int = 100
    get() = field                // 後端變量
    set(value) {
        if (value < 10) {       // 如果傳入的值小於 10 返回該值
            field = value
        } else {
            field = -1         // 如果傳入的值大於等於 10 返回 -1
        }
    }

getter和setter方法的權限

setter方法:支持加屬性 private protected 等

getter方法:的權限跟所處的變量一致

注意

field屬性的作用域

  • 僅僅存在於當前屬性的setter/getter方法中
  • 就像當前屬性的影子一樣。
  • 它是自動生成

範例三

class Person {
    var lastName: String = "zhang"
        get() = field.toUpperCase()   // 將變量賦值後轉換爲大寫
        set

    var color: String = "#ff0000"
        set(value){ field = value }
        get() = field
    
    var number : Int = 100
    
    var no: Int = 100
        get() = field              // 後端變量
        set(value) {
            if (value < 10) {       // 如果傳入的值小於 10 返回該值
                field = value
            } else {
                field = -1         // 如果傳入的值大於等於 10 返回 -1
            }
        }

    var heiht: Float = 145.4f
        private set
}
fun main(args: Array<String>) {
    var person: Person = Person()
    person.lastName = "wang"
    println("lastName:${person.lastName}")
    person.no = 9
    println("no:${person.no}")
    println("no:${person.number}")
    person.color = ("#ffffff")
    println("no:${person.color}")
    println("no:${person.heiht}")
}

輸出

lastName:WANG
no:9
no:100
no:#ffffff
no:145.4


幕後字段

field標識符在訪問器中引用
當一個屬性需要一個幕後字段時,Kotlin 會自動提供

var counter = 0 // 注意:這個初始器直接爲幕後字段賦值
set(value) {
    if (value >= 0) field = value
}

只能用在屬性的訪問器內

什麼時候沒有field字段?

field字段是需要特定的條件下才能產生的。部分情況不會產生。

  • 不使用默認實現
  • 且不通過 field 引用幕後字段

比如以下這種情況

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

幕後屬性

定義

幕後屬性是我們自己定義的,一般用於這種情況

  • 對外表現爲只能讀,不能寫
  • 對內表現可以任意讀寫

相當於,一個變量即是 val 又是var,java可以通過函數實現

範例

class Demo{
    val size get() = _size
    private var _size:Int = 0
    fun rise() {
        _size = _size + 1
    }
}

fun main(args: Array<String>) {
    val demo = Demo()
    println(demo.size)
    demo.rise()
    println(demo.size)
    demo.rise()
    println(demo.size)
}

輸出

0
1
2

對於 JVM 平臺

通過默認 getter 和 setter 訪問私有屬性會被優化
不會引入函數調用開銷


幕後字段與幕後屬性的區別

幕後字段:Kotlin內置提供給我們的
幕後屬性:是我們設計類屬性時的一種實現方法而已


編譯期常量

使用 const 修飾符標記爲 編譯期常量
使用條件

  • 位於頂層或者是 object 聲明 或 companion object 的一個成員
  • 以 String 或原生類型值初始化
  • 沒有自定義 getter
const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"

與java一樣
常量可以用在註解中


延遲初始化屬性與變量

一般地,屬性聲明爲非空類型必須在構造函數中初始化
但是,你是通過註解,或者某setup函數來初始化的
你不能在構造函數內提供一個非空初始器
仍然想在類體中引用該屬性時避免空檢測

解決辦法

用 lateinit 修飾符標記該屬性

適用範圍

  • 非空類型
  • 非原生類型

注意

在初始化前訪問一個 lateinit 屬性會拋出一個特定異常,
該異常明確標識該屬性被訪問及它沒有初始化的事實。

檢測一個 lateinit var 是否已初始化(自 1.2 起)

用 .isInitialized

if (foo::bar.isInitialized) {
    println(foo.bar)
}

僅對可詞法級訪問的屬性


覆蓋屬性

可以參考 《類篇》


委託屬性

即delegate
用到了關鍵字 by
範例

class Example {
    var p: String by Delegate()
}

後續我們會講到

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章