本系列文章來學習 Kotlin 和 Anko 插件 通過 Kotlin 開發(fā)一個 Android 項目。
Kotlin-Anko學習(1) Kotlin榄檬、Anko 介紹
Kotlin-Anko學習(2) Kotlin 語法基礎-基本類型
Kotlin-Anko學習(3) Kotlin 語法基礎-關鍵字 package卜范、Import、if鹿榜、when海雪、for、while舱殿、return奥裸、break、continue
Kotlin-Anko學習(4) Kotlin語法-類沪袭、繼承湾宙、抽象類
Kotlin-Anko學習(5) Kotlin語法-屬性、字段冈绊、接口
屬性
Kotlin 中 屬性的聲明包括:關鍵字 val或var 侠鳄、屬性名、屬性類型死宣、初始器伟恶、訪問器組成 如下:
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
//示例
var allByDefault:Int ?= null
get() = if(field ==null){
0
}else{
field
}
// set訪問器只有在var 可變屬性中存在 val 沒有set訪問器
set(value){
field = if(value==null){
0
}else{
value-2
}
}
以上示例是完整的屬性聲明,一般初始器(initializer)毅该、訪問器getter 和 setter 都是可選的博秫。屬性類型如果可以從初始器 、訪問器中推斷出來眶掌,也可以省略挡育。總之遵循根據(jù)上下文推算出來的就可以不去特意聲明朴爬。這也是kotlin 簡潔性的特征之一即寒。
- Kotlin中,給屬性直接賦值,或者調用屬性的值蒿叠,其實質是調用了訪問器 get、set 方法蚣常,這點跟java是不同的市咽。
- 如果你需要改變一個訪問器的可見性或者對其注解,但是不需要改變默認的實現(xiàn)抵蚊, 你可以定義訪問器而不定義其實現(xiàn)施绎。
也就是在類外禁止改變 屬性var 的值,可以將其聲明為val 或者 自定義var set訪問器的可見性 private 如下
class Kot{
var allByDefault:Int ?= null
get() = if(field ==null){
0
}else{
field
}
//用private進行修飾
private set(value){
field = if(value==null){
0
}else{
value-2
}
}
}
//Kot.allByDefault= 1//error: 不允許設置
幕后字段
- Kotlin 中不能直接聲明字段贞绳,當屬性需要字段時谷醉,會提供一個幕后字段。用field標識
- Kotlin 中存在幕后字段的情況:
- 屬性訪問器默認實現(xiàn)會隱式使用幕后字段冈闭。
- 自定義訪問器通過field顯式引用幕后字段俱尼。
var age : Int = 15
//上面這句代碼其實和下面的代碼是等價的,沒有區(qū)別萎攒。
var age: Int = 15
set(value) {
field = value
}
get() {
return field
}
上面是kotlin對于幕后字段的使用遇八,如何理解屬性和字段呢? 比如在java中耍休,一個person類中刃永,在外部看來有一個getAge()和setAge(int age)方法,說明person有一個age 屬性羊精, 但是不能說person中一定有age這個字段斯够。而在kotlin 中 可以通過關鍵字var/val 聲明屬性,但是沒有關鍵字來聲明字段:如下:
//java
class Person {
private int age =15; //age 是一個字段
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
new Person.getAge()//獲取person的age屬性
//kotlin
class Person {
var age: Int = 15//person 的一個age屬性
set(value) {
field = value // field 為一個幕后字段的標識
}
get() {
return field
}
}
以上兩段代碼生成的.class文件是相同的喧锦。這就是我對 backing field 的理解读规。
幕后屬性
kotlin 中可以通過聲明一個私有的屬性作為一個幕后屬性
class Base{
private var _table: Map<String, Int>? =null
public val table: Map<String, Int>
get() {
if (_table == null) {
_table = HashMap() //類型參數(shù)已推斷出
}
return _table ?: throw AssertionError("Set to null by another thread")
}
}
fun main(args: Array<String>) {
var base = Base()
println("table = ${base.table}" )
}
幕后屬性的使用與java的字段相似,解決隱式幕后字段不能處理的問題燃少,通過私有屬性的get/set來優(yōu)化掖桦。也是Kotlin對于空指針的一種解決方案。
編譯期常量
通過 const 修飾的屬性 滿足如下條件:
- 位于頂層或者是 object 的一個成員
- 用 String 或原生類型 值初始化
- 沒有自定義 getter
//編譯期常量
const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"
@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { …… }
延遲初始化屬性與變量
通過 lateinit 修飾的屬性 滿足如下條件:
- 只能用于在類體中的屬性(不是在主構造函數(shù)中聲明的 var 屬性供汛,并且僅當該屬性沒有自定義 getter 或 setter 時)
- 用于頂層屬性與局部變量枪汪。該屬性或變量必須為非空類型,并且不能是原生類型
- 在初始化前訪問一個 lateinit 屬性會拋出一個特定異常怔昨,明確標識該屬性被訪問及它沒有初始化
//延遲性屬性的使用如下:
public class MyTest {
class TestSubject{
fun method(){
println("TestSubject.method()")
}
}
lateinit var subject: TestSubject
fun setup() {
subject = TestSubject()
}
fun test() {
subject.method() // 直接解引用
}
}
fun main(args: Array<String>) {
var test = MyTest()
test.setup()//如果不調用setup()初始化雀久,會報如下錯誤
test.test()
}
初始化前使用延遲性屬性,會報如下錯誤:
Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property subject has not been initialized
at MyTest.test(Simplest version.kt:12)
at Simplest_versionKt.main(Simplest version.kt:18)
檢測一個 lateinit var 是否已初始化 可以通過引用屬性的.isInitialized來做判斷:
class Foo {
lateinit var bar: String
fun setup() {
println("sssss ${this::bar.isInitialized}" )
bar="value"
println("xxxxx ${this::bar.isInitialized}")
}
}
fun main(args: Array<String>) {
Foo().setup()
}
輸出結果:
sssss false
xxxxx true
屬性和字段的基本使用就寫完了趁舀,由于篇幅問題赖捌,再加上一個接口的學習。
接口
接口的定義與實現(xiàn)
Kotlin 中使用關鍵字 interface 來定義接口,包含抽象方法的聲明越庇、也可以有方法的實現(xiàn)罩锐。這就跟java有區(qū)別了,如果聲明的方法沒有方法體卤唉,默認是抽象方法涩惑,子類必須實現(xiàn),有方法體的子類可以選擇性實現(xiàn)桑驱。
interface MyInterface {
//默認抽象方法
fun bar()
fun foo() {
// 可選的方法體
}
}
class Child : MyInterface {
override fun bar() {
// 方法體
}
}
接口中定義的屬性
Kotlin 中接口可以定義抽象屬性 或者提供訪問器的實現(xiàn)竭恬,但是訪問器的實現(xiàn)不能包含幕后字段(上面有對幕后字段的理解)。
interface MyInterface {
val prop: Int // 抽象的
val propertyWithImplementation: String
get() = "foo"
fun foo() {
println(prop)
println(propertyWithImplementation)
}
}
class Child : MyInterface {
override val prop: Int = 29
override val propertyWithImplementation: String
get() = super.propertyWithImplementation
}
fun main(args: Array<String>) {
println(Child().propertyWithImplementation)
Child().foo()
}
輸出結果:
foo
29
foo
解決覆蓋沖突
現(xiàn)多個接口時熬的,可能會遇到同一方法繼承多個實現(xiàn)的問題痊硕,在類的繼承中已經(jīng)講解過,請參考上一篇押框。
參考
http://www.reibang.com/writer#/notebooks/19396434/notes/22305825/preview
https://liyuanbiao.wordpress.com/2017/07/30/ru-he-li-jiekotlin-zhong-shu-xing/