Kotlin從入門到放棄只基礎篇(五)
可見性修飾詞
在Kotlin中存在四種修飾詞:public, private,protected,internal.其中默認的修飾詞是public。它們用於修飾類、對象、接口、構造函數、屬性以及它們的set方法。
包
在Kotlin中函數、屬性、類、對象以及接口可以在包級別進行定義。
四種修飾詞在包級別的作用域:
♣ public :默認修飾符,在任何位置都能被訪問。
♣ private:只能在當前源文件中被使用
♣ internal:能夠在同一個模塊中被使用
♣ protected: 無效修飾符,只能用於類和接口內部。
package demo
private fun test1(){} // 只能在當前文件中被訪問
public var test2 : String="test" //能在任何位置被訪問
internal val test3=6//能夠在同一個module中被訪問
類和接口
四種修飾詞在類和接口中的作用域:
♣ public: 默認修飾符,在任何位置都能被訪問
♣ private: 只能在這個類中被訪問
♣ internal: 能夠在同一個模塊中被使用
♣ protected: 只能被當前類以及子類訪問
open class Person{
private val age=15//只能夠在當前類中被訪問
public val name ="小明"//能夠在任何位置被訪問
protected val sex="男"//只能夠在當前類及其子類中被訪問
internal val height=175//能夠在同一個Module中被訪問
}
構造器
所有的構造函數默認是public,當使用其他修飾符修飾時必須明確添加一個constructor關鍵字:
class Person private constructor (age:Int,name:String){
//...
}
模塊
Kotlin中的模塊是指一系列的Kotlin文件編譯在一起的源碼:
♣一個IntelliJ IDEA 模塊
♣ 一個Maven工程,或者Gradle工程
♣ 通過Ant任務的一次調用編譯的一組文件
局部聲明
局部變量、函數和類是不能指定修飾符的。
擴展
與C#和Gosu類似,Kotlin也提供了一種在不繼承父類也不使用其他設計模式的情況下對指定的類進行擴展的方法。我們可以通過擴展的特殊聲明來實現。擴展分爲函數擴展和屬性擴展
函數擴展
聲明函數擴展時,我們需要在函數前面指定被擴展的對象。例如:
fun MutableList<Int>.swap(x:Int,y:Int){//交換list中x項和y項的值
val temp = this[x]//this對應List
this[x] = this[y]
this[y] = temp
}
♣ 在函數擴展中this關鍵字對應函數接收者對象。
擴展是被靜態解析的
擴展並沒有修改被擴展的類。並沒有在該類中插入一個新的成員,只是讓這個實例對象能夠通過“.”調用該擴展成員。
擴展函數是靜態解析的,他並不是接收者類型的虛擬成員。意味着調用擴展函數時,具體調用哪個擴展函數由調用函數的對象表達式來決定,而不是動態的類型決定的。例如,我們定義兩個對象。Teacher和Person其中Person是Teacher的父類。兩對象均聲明瞭擴展函數doFly(),然後聲明fly()方法傳入Person對象的形參調用doFly方法。
open class Person{}
class Teacher : Person(){}
fun Person.doFly() {//Person類的doFly()擴展方法
println("Person fly")
}
fun Teacher.doFly() { //Teacher類的doFly()擴展方法
println("Teacher fly")
}
fun fly(person : Person){
person.doFly()
}
fun main(args : Array<String>){//測試
var teacher:Teacher = Teacher()
fly(teacher)
}
上述代碼的輸出結果爲”Person fly”,由此可以看出調用哪個擴展函數是由聲明的參數類型決定的
成員函數和擴展函數
當擴展函數的接收者對象存在與擴展函數相同的成員函數。調用該函數時到底調用的是成員函數還是擴展函數:
open class TestDemo {
test(){//成員函數
println("成員函數")
}
}
fun TestDemo.test(){//擴展函數
println("擴展函數")
}
fun main(args : Array<String>){
var person :Person =Person()
person.test()
}
上述代碼的輸出結果爲”成員函數”,由此可以看出當存在成員函數和擴展函數的名稱相同時,當調用該函數時,優先調用成員函數。
可空的接收者
擴展成員的接收者可以爲空。這樣我們仍然可以用一個空的對象來調用該擴展成員,在該方法中利用”null==this”的判斷進行邏輯操作。這樣的話我們可以在任意地方使用該擴展成員而不擔心出現空指針的錯誤。
屬性擴展
Kotlin中也支持屬性的擴展。
但是由於擴展屬性並不能夠給接受對象提供一個真正的成員屬性,所以擴展屬性不能擁有備份字段。因此初始化函數中不能夠擁有擴展屬性,擴展屬性只能夠通過明確的getter和setter方法來進行定義。這也意味着擴展屬性只能夠被聲明爲val,。如果被聲明爲var,進行初始化時會報異常錯誤。
伴隨對象的擴展
如果定義了伴隨對象,則同樣可以對伴隨對象進行擴展。
open class TestDemo{
companion object{}
}
fun TestDemo.Companion.expandFun(){
}
調用時和普通伴隨對象的成員相同:
TestDemo.expandFun()
擴展的域
大多數時候我們在包(top level) 定義擴展:
package test.demo
fun Person.swim(){//在test.demo包下定義swim的擴展
}
當我們在test.demo包外使用上面聲明的擴展時,我們需要使用import 關鍵字導入該擴展
package com.example
import test.demo.swim//導入該擴展
import test.demo.*//也可以通過導入test.demo包下的所有數據的方式導入該擴展
fun main(args : Array<String>){
var person : Person = Person()
person.swim()//使用該擴展
}
數據類
我們通常創建一些保存數據的類。在Kotlin中這樣的類被稱爲data類,用data關鍵字進行標註:
data class Person (val name: String , val age :Int)
編譯器會對主構造函數中聲明的所有屬性添加如下方法:
♣ equals():hashCode 函數
♣ toString():輸出的格式爲Person(name=小明,age=18)
♣ compontN(): 對應按生命順序出現的所有屬性
♣ copy()
如果在該數據類或其基類中重寫了上述方法,則已重寫的方法爲準
數據類聲明條件
♣ 主構造函數中至少有一個函數
♣ 主構造函數中所有的屬性必須標記val或者var關鍵字
♣ 數據類不能是抽象類、open類、封閉(sealed)類或者內部(inner)類
♣ 數據類不能繼承自其他類(接口除外)
♣ 如果數據類有無參構造函數,則需要在主構造函數中將屬性初始化。
data class Person(val name:String="小明", val age:Int=18)
複製
當我們需要對數據類中的一些屬性進行修改但是保持其他部分不變,則需要使用到copy()函數。例如:
val xiaoming = Person(name="小明",age=18)
val xiaoli = (name="小李")//只是改變了name屬性,age屬性還是18
多重聲明
上面提到了componentN()組件函數,函數名中的1到N對應屬性中的聲明順序,因此我們可以在數據類的多重聲明中使用:
val xiaoming = Person("小明", 18)
val (n,m) = xiaoming
println("$n,is $m years old")
上述代碼打印結果爲”小明,is 18 years old”