Kotlin的類型可以有屬性。 這些可以聲明為可變的,使用var關鍵字或使用val關鍵字只讀。
class Address {
var name: String = ...
var street: String = ...
var city: String = ...
var state: String? = ...
var zip: String = ...
}
要使用一個屬性,簡單地通過名稱引用它,就好像它是Java中的一個字段:
fun copyAddress(address: Address): Address {
val result = Address() // there's no 'new' keyword in Kotlin
result.name = address.name // accessors are called
result.street = address.street
// ...
return result
}
聲明屬性的完整語法是 -
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
初始化程序,getter和setter是可選的。 如果可以從初始化程序(或從getter返回類型,如下所示)推斷屬性類型,則屬性類型是可選的。
例子:
var allByDefault: Int? // error: explicit initializer required, default getter and setter implied
var initialized = 1 // has type Int, default getter and setter
只讀屬性聲明的完整語法與可變的屬性聲明的不同之處,有兩種方式:它以val而不是var開頭,不允許setter:
val simple: Int? // has type Int, default getter, must be initialized in constructor
val inferredType = 1 // has type Int and a default getter
可以在一個屬性聲明中寫出自定義訪問器,非常像普通功能。 以下是一個定制getter的例子:
val isEmpty: Boolean
get() = this.size == 0
自定義設置器(setter)如下所示:
var stringRepresentation: String
get() = this.toString()
set(value) {
setDataFromString(value) // parses the string and assigns values to other properties
}
按照慣例,setter參數(shù)的名稱是value,可以選擇或使用不同的名稱。
從Kotlin 1.1起,如果可以從getter推斷屬性類型,則可以省略它:
val isEmpty get() = this.size == 0 // has type Boolean
如果需要更改訪問器的可見性或注釋它,但不需要更改默認實現(xiàn),可以定義訪問器而不定義其主體:
var setterVisibility: String = "abc"
private set // the setter is private and has the default implementation
var setterWithAnnotation: Any? = null
@Inject set // annotate the setter with Inject
Kotlin的類不能有字段。 但是,有時在使用自定義訪問器時需要有一個后備字段。 為了這些目的,Kotlin提供了可以使用字段標識符訪問的自動備份字段:
var counter = 0 // the initializer value is written directly to the backing field
set(value) {
if (value >= 0) field = value
}
field標識符只能在屬性的訪問器中使用。
如果屬性使用至少一個訪問器的默認實現(xiàn),或者自定義訪問器通過field標識符引用它,則將為屬性生成后備字段。
例如,在以下情況下,將不會有后備字段:
val isEmpty: Boolean
get() = this.size == 0
如果想做一些不符合這個“隱性后備字段”方案的東西,總是可以回到擁有一個后備屬性:
private var _table: Map<String, Int>? = null
public 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中的一樣,因為使用默認getter和setter的私有屬性的訪問被優(yōu)化,因此不會引入函數(shù)調(diào)用開銷。
在編譯時已知其值的屬性可以使用const修飾符標記為編譯時常數(shù)。 這些屬性需要滿足以下要求:
getter這些屬性可以在注釋中使用:
const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"
@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { ... }
通常,聲明為非空類型的屬性必須在構(gòu)造函數(shù)中進行初始化。 然而,這通常不方便。 例如,可以通過依賴注入或單元測試的設置方法初始化屬性。 在這種情況下,不能在構(gòu)造函數(shù)中提供非空的初始值設置,但是仍然希望在引用類的正文中的屬性時避免空檢查。
要處理這種情況,可以使用lateinit修飾符標記屬性:
public class MyTest {
lateinit var subject: TestSubject
@SetUp fun setup() {
subject = TestSubject()
}
@Test fun test() {
subject.method() // dereference directly
}
}
修飾符只能用于在一個類的主體內(nèi)聲明的var屬性(不在主構(gòu)造函數(shù)中),并且只有當該屬性沒有自定義的getter或setter時才可以使用。 屬性的類型必須為非空值,并且不能為原始類型。
在初始化之前訪問一個lateinit屬性會引發(fā)一個特殊的異常,清楚地標識被訪問的屬性以及它還沒被初始化的事實。
最常見的屬性只是讀取(也可能寫入)支持字段。 另一方面,使用定制getter和setter可以實現(xiàn)屬性的任何行為。屬性如何運作有一些共同的模式。 幾個例子:懶值,通過給定的鍵讀取映射,訪問數(shù)據(jù)庫,通知訪問者等。