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)
宣告屬性的完整語法如下,語法中的初始化語句,getter
和setter
都是可選的
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
)和自定義setter
,setter
的引數名預設是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中,並不允許使用區域性變數,在自定義getter
和setter
的時候,可以使用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
檔案中
Top-level
呼叫的時候類似於呼叫擴充套件函式那樣,直接呼叫屬性或者函式。
loge("top-level", prop)
demo()
延遲初始化屬性(Late-Initialized Properties)
在Kotlin中,宣告為具有非空型別的屬性必須在建構函式中初始化,但是往往不希望在建構函式中初始化,例如在通過依賴注入或單元測試的設定方法來初始化屬性的時候,不能在構造器中提供一個非空的初始化語句,為了處理這種情況,就要在屬性上加lateinit
關鍵字來延遲初始化
public class MyTest {
lateinit var subject: TestSubject
@SetUp fun setup() {
subject = TestSubject()
}
@Test fun test() {
subject.method()
}
}
lateinit
只能夠在var
型別的屬性中,不能用於建構函式,而且屬性不能有自定義的getter
和setting
,這些屬性必須是非空型別,並且不能是基本型別。
如果在一個延遲初始化的屬性初始化前呼叫,會導致一個特定異常,呼叫的時候值還沒有初始化.