1. 程式人生 > >Kotlin學習(三): 屬性和欄位

Kotlin學習(三): 屬性和欄位

Kotlin

宣告屬性(Declaring Properties)

Kotlin中可以使用var關鍵字宣告可變屬性,或者用val關鍵字宣告只讀屬性,屬性的型別在後面,變數名在簽名,中間加冒號和空格。

public class Address {
    public var name: String = ...
    public var street: String = ...
    public var city: String = ...
    public var state: String? = ...
    public var zip: String = ...
}

呼叫的時候與Java一樣,通過變數名直接使用一個屬性

fun copyAddress(address: Address): Address {
    val result = Address() // Kotlin不需要使用new關鍵字
    result.name = address.name
    result.street = address.street
    // ...
    return result
}

Getter和Setter(Getters and Setters)

宣告屬性的完整語法如下,語法中的初始化語句,gettersetter都是可選的

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

如果屬性型別可以從初始化語句或者類的成員函式中推斷出來,那就可以省去型別,val不允許設定setter函式,以為它是隻讀的。

var allByDefault: Int? // 錯誤: 需要一個初始化語句, 預設實現了 getter 和 setter 方法
var initialized = 1 // 型別為 Int, 預設實現了 getter 和 setter
val simple: Int? // 型別為 Int ,預設實現 getter ,但必須在建構函式中初始化
val inferredType = 1 // 型別為 Int 型別,預設實現 getter

可以自定義訪問器(getter)和自定義settersetter的引數名預設是value,也可以自定義

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

var stringRepresentation: String
    get() = this.toString()
    set (value) {
        setDataFormString(value) // 格式化字串,並且將值重新賦值給其他元素
    }

如果需要設定訪問器的可見性或者設定註解,又不改變原來的實現,則可以設定一個不帶函式的訪問器

var setterVisibility: String = "abc" // 非空型別必須初始化
    private set // setter是私有的並且有預設的實現

var setterWithAnnotation: Any? = null // 設定為可空
    @Inject set // 用 Inject 註解 setter

備用欄位(Backing Fields)

Kotlin中,並不允許使用區域性變數,在自定義gettersetter的時候,可以使用field來起到區域性變數的作用。

var counter = 0 //初始化值會直接寫入備用欄位
    get() = field
    set(value) {
        if (value >= 0)
            field  = value
    }

編譯器會檢查訪問器的程式碼,如果使用了備用欄位(或者訪問器是預設的實現邏輯),就會自動生成備用欄位,否則就不會。

// 這種情況並不需要備用欄位,所有不會生成備用欄位
val isEmpty: Boolean
    get() = this.size == 0

注意:field識別符號只允許在屬性的訪問器函式內使用.

備用屬性(Backing Properties)

備用屬性,可以看作是備用變數(Backing Fields)的變種,其實際上也是隱含試的對屬性值的初始化宣告,避免了空指標。

private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
    get() {
        if (_table == null) {
            _table = HashMap() // 引數型別是自動推導
        }
        return _table ?: throw AssertionError("Set to null by another thread")
    }

不管是備用變數或者備用屬性,都是Kotlin對於空指標的一種解決方案,可以避免函式訪問私有屬性而破壞它的結構。

編譯時常量(Compile-Time Constants)

那些在編譯時就能知道具體值的屬性可以使用const修飾符標記為編譯時常量. 這種屬性需要同時滿足以下條件:
- 頂層或物件的成員(Top-level or member of an object)
- 以String或基本型別進行初始化
- 沒有自定義getter

這種屬性可以當作註解使用

const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"
@Deprected(SUBSYSTEM_DEPRECATED) fun foo() { ... }

Top-level(頂級)

Top-level屬性或者方法,與class同級,如下面所示,類名是Kot

package foo.bar

val prop: String = "top-level-prop"
fun demo() {
    loge("top-level", "top-level-demo()")
}

class Kot {
    fun v() {
        loge("top-level", prop)
        demo()
    }
}

在編譯成class的時候,會把Top-level的屬性和函式建立到以類名+Kt為名的class檔案中

KotKt.class

Top-level呼叫的時候類似於呼叫擴充套件函式那樣,直接呼叫屬性或者函式。

loge("top-level", prop)
demo()

log

延遲初始化屬性(Late-Initialized Properties)

在Kotlin中,宣告為具有非空型別的屬性必須在建構函式中初始化,但是往往不希望在建構函式中初始化,例如在通過依賴注入或單元測試的設定方法來初始化屬性的時候,不能在構造器中提供一個非空的初始化語句,為了處理這種情況,就要在屬性上加lateinit關鍵字來延遲初始化

public class MyTest {
    lateinit var subject: TestSubject

    @SetUp fun setup() {
        subject = TestSubject()
    }

    @Test fun test() {
        subject.method() 
    }
}

lateinit只能夠在var型別的屬性中,不能用於建構函式,而且屬性不能有自定義的gettersetting,這些屬性必須是非空型別,並且不能是基本型別。

如果在一個延遲初始化的屬性初始化前呼叫,會導致一個特定異常,呼叫的時候值還沒有初始化.