kotlin中功能與特定函數(shù)命名相關漫贞,而不是與特定類型綁定,這種技術稱為約定育叁。kotlin使用約定的原則迅脐,并不像Java那樣需要依賴類型,因為它允許開發(fā)人員適應現(xiàn)有的java類豪嗽,來滿足kotlin語言的特性要求谴蔑。由類實現(xiàn)的接口是接口集是固定的,而kotlin不能為了實現(xiàn)其它接口而修改現(xiàn)有類龟梦。同時隐锭,kotlin也可以通過擴展函數(shù)的機制來未現(xiàn)有的類增添新方法〖品。可以把任意約定方法定義為擴展函數(shù)钦睡。
重載算術運算符
算術運算符最能直觀的反映kotlin的約定。在kotlin中躁倒,可以使用+=等基本運算符對集合或者對象進行操作荞怒。
data class Point(val x:Int,val y:Int) {
//使用operator關鍵字聲明plus函數(shù)
//用于重載運算符的所有函數(shù)都需要用它標記洒琢,表示約定而不是同名函數(shù)
operator fun plus(other:Point): Point {
//坐標分別相加返回新的Point
return Point(x+other.x,y+other.y)
}
}
val p1 = Point(10, 20)
val p2 = Point(20, 30)
//等同 p1.plus(p2)
println(p1 + p2) //Point(x=30, y=50)
也可以定義為擴展函數(shù)
//定義名為plus方法
operator fun Point.plus(other:Point): Point {
//坐標分別相加返回新的Point
return Point(x+other.x,y+other.y)
}
使用擴展函數(shù)來定義約定是常用的模式。
在kotlin中不能定義自己的運算符褐桌。kotlin限定了重載的運算符且需要在類中定義對應名字的函數(shù)衰抑。
可重載的二元算術運算符總覽
表達式 | 函數(shù)名 |
---|---|
a*b | times |
a/b | div |
a%b | mod |
a+b | plus |
a-b | minus |
自定義類型的運算符與標準數(shù)字類型運算符有著相同優(yōu)先級。
自定義運算符允許運算數(shù)是不同類型荧嵌。
operator fun Point.times(scale:Double): Point {
return Point((x*scale).toInt(),(y*scale).toInt())
}
val p1 = Point(10, 20)
println(p1*1.5)//Point(x=15, y=30)
然而需要注意的是呛踊,kotlin運算符不自動支持交換性(交換運算符左右)。如果想使用p1.5和1.5p,需要定義單獨的運算符operator fun Double.times(p:Point):Point啦撮。
運算符函數(shù)返回類型也可以不同于任一運算數(shù)類型谭网。
operator fun Char.times(count:Int):String{
//repeat是擴展函數(shù),重復搞事情
return toString().repeat(count)
}
println('A'*3) //AAA
函數(shù)名 | 位運算 |
---|---|
shl | 帶符號左移 |
shr | 帶符號右移 |
ush | 無符號右移 |
and | 按位與 |
or | 按位或 |
xor | 按位異或 |
inv | 按位取反 |
和普通函數(shù)一樣逻族,可以重載operator函數(shù):定義多個同名函數(shù)蜻底,但參數(shù)類型不一樣的函數(shù)骄崩。
kotlin沒有用于位運算的特殊運算符聘鳞,所以也不存在自定義去定義。但是它支持中綴調用要拂。
kotlin提供位運算的完整函數(shù)表(筆者暫時不懂位運算抠璃,權作記錄)。
函數(shù)名 | 位運算 |
---|---|
shl | 帶符號左移 |
shr | 帶符號右移 |
ush | 無符號右移 |
and | 按位與 |
or | 按位或 |
xor | 按位異或 |
inv | 按位取反 |
重載復合賦值運算符
通常情況下脱惰,定義運算符函數(shù)時搏嗡,也支持復合賦值運算符(+=,-=)。
var p1=Point(10,20)
val p2=Point(20,30)
//僅對可變變量有效
p1+=p2
println(p1)//Point(x=30, y=50)
在一些情況下拉一,+=運算符可以修改使用它的變量所引用的對象采盒,但不會重新分配引用。比如將一個元素添加到可變集合蔚润。
val numbers=ArrayList<Int>()
numbers+=42
println(numbers[0])//42
如果定義返回值為Unit磅氨,名為plusAssign的函數(shù)會在用到+=運算符的地方調用它。其它二元算術運算符也有相對應的函數(shù)嫡纠。
kotlin標準庫可變集合定義了plusAssign函數(shù)烦租。
//簡化
numbers.plusAssign(42)
值得注意的是當在代碼調用+=的時候,理論上plus和plusAssign都可能被調用除盏。這種情況下編譯器會拋異常叉橱。用val去替代var,那么plusAssign不再適用。但是總的來說盡量不要同時使用這兩個函數(shù)者蠕。
kotlin+和-運算符總是會返回一個新的集合窃祝。+=,-=用于可變集合時候始終會在一個地方修改它們踱侣,它們用于只讀集合時候粪小,會返回一個修改過的副本(即引用集合必須聲明為var的時侯)甩栈,作為運算數(shù),可以使用單個元素糕再,也可以使用元素類型一致其它集合量没。
val list = arrayListOf(1, 2)
// list被修改
list += 3
//返回包含所有元素的新列表
val newList = list + listOf(4, 5)
println(list)//[1, 2, 3]
println(newList)//[1, 2, 3, 4, 5]
重載一元運算符
重載一元運算符實現(xiàn)方式相同
//一元運算符無參數(shù)
operator fun Point.unaryMinus():Point{
//坐標去反
return Point(-x,-y)
}
val p1 =Point(10,20)
println(-p1)//Point(x=-10, y=-20)
可重載的一元算法的運算符
表達式 | 函數(shù)名 |
---|---|
+a | unaryPlus |
-a | unaryMinus |
!a | not |
++a,a++ | inc |
--a,a-- | dec |
前綴運算符(先計算在取值)和后綴運算符(先取值在計算)也與基本數(shù)據(jù)類型運算符相同。
重載比較運算符
與算術運算符相同突想,在kotlin中可以對任何對象使用比較運算符(==殴蹄,+=,>,<等)猾担。
等號運算符
kotlin中使用==袭灯,!=運算符绑嘹,都將被轉換成equals方法調用(約定原則)稽荧,同時都可以用于可空的運算數(shù)(即會檢查運算數(shù)是否為null),不同的是結果相反。
p1==p2
//實際上是
p1.equals(p2)?:(p2==null)
數(shù)據(jù)類會自動實現(xiàn)equals等函數(shù)工腋,非數(shù)據(jù)類手動實現(xiàn)
override fun equals(other: Any?): Boolean {
//===恒等運算符(無法重載)姨丈,等同Java的==,通常equals之后使用來優(yōu)化代碼
if (other===this) return true //檢查參數(shù)與thi是否同一個對象
if (other !is Point) return false //檢查參數(shù)類型
return other.x==x&&other.y==y //智能轉換為Point來訪問
}
val p1 = Point(10, 20)
val p2 = Point(20, 30)
println(p1== Point(10,20)) //true
println(p2!= Point(5,5)) //true
println(null==p1) //false
equals函數(shù)之所以被標記為override是因為與約定不同的是擅腰,它是實現(xiàn)在Any類中定義的(kotlin中所有對象都支持等式比較)蟋恬,operator修飾符也適用所有實現(xiàn)或重寫它的方法。equals不能實現(xiàn)為擴展函數(shù)趁冈,因為繼承自Any類的實現(xiàn)始終優(yōu)先于擴展函數(shù)歼争。
排序運算符 :compaerTo
kotlin支持和Java相同的Comparable接口。但是接口中定義的compareTo方法可以按照約定調用渗勘,比較運算符(<,>,<=,>=)的使用將被轉換為compareTo,它的返回類型必須為Int沐绒。
p1 < p2
//等價于
p1.compareTo(p2)<0
//換言之
a>=b
//等價于
a.compareTo(b)>=0
以之前Person為例子(先比較姓氏,如果姓氏相同旺坠,比較名字)
class Person(
val firstName:String,val lastName:String
) :Comparable<Person>{
override fun compareTo(other: Person): Int {
//按順序調用給定方法并比較它們值
return compareValuesBy(this,other,Person::lastName,Person::firstName)
}
}
val p1 = Person("JoJo", "小白")
val p2 = Person("Bob", "小黑")
println(p1<p2) //true
compareValuesBy可以簡潔的實現(xiàn) compareTo方法乔遮。該函數(shù)用來計算比較值一系列回調,按順序依次調用回調(兩兩比較价淌,相同繼續(xù)申眼,不同返回值,沒有回調返回0)蝉衣。
實現(xiàn)Comparable接口的對象不僅在kotlin中用來比較括尸,也可以被Java函數(shù)(如對集合進行排序)進行比較,與equals一樣病毡,operator修飾符在基類接口已經實現(xiàn)濒翻,無需重復。
//直接比較字符串
println("abc"<"cbb")//true
集合與區(qū)間的約定
處理集合最常見一些操作是通過下標來獲取設置元素,以及檢查元素是否屬于當前集合有送。所有這些操作都支持運算符語法:要通過下標獲取或者設置元素淌喻,可以使用語法ab∪刚可以使用in運算符來檢查元素是否在集合或區(qū)間內裸删,也可以迭代集合
下標訪問:“get”,"set"
如前所述,在kotlin中可以用類似Java中數(shù)組的方式來訪問map中的元素阵赠。
val map = mutableMapOf("1" to "one","2" to "two")
//根據(jù)key取值
println(map["1"] ) //one
//改變map的值
map["1"]="一";
println(map["1"]) //一
下標運算符也是一個約定涯塔。讀取元素它會轉變成get,寫入元素會轉變成set。Map和MutableMap接口定義這些方法清蚀。我們也可以給自定義的類添加類似方法匕荸。
//定義名為"get"的運算符函數(shù)
operator fun Point.get(index:Int):Int{
//根據(jù)索引返回對應坐標
return when(index){
0->x
1->y
else ->
throw IndexOutOfBoundsException("Invalid coordinate $index")
}
val p1 =Point(10,20)
println(p1[0]) //10
println(p1[1]) //20
僅僅只需要定義一個名為get(參數(shù)類型可以是任何類型)的函數(shù),并使用operator修飾符修飾即可枷邪。
x[a,b]
//等價于
x.get(a,b)
set函數(shù)同理榛搔,不過需要的是它是可變的。
data class MutablePoint(var x: Int, var y: Int) {
}
operator fun MutablePoint.set(index:Int,value:Int){
when(index){
0->x=value
1->y=value
else ->
throw IndexOutOfBoundsException("Invalid coordinate $index")
}
}
val p =MutablePoint(10,20)
p[0] = 40;
p[1]=80
println(p) //MutablePoint(x=40, y=80)
同理东揣,set的最后一個參數(shù)用來接收賦值語句中(等號)右邊的值践惑,其它參數(shù)則作為下標。
x[a,b] = c
//等價于
x.set(a,b,c)
"in"的約定
集合支持的另一個運算符是in運算符救斑,用于檢查某個對象是否屬于集合童本。相應的函數(shù)叫contains。
舉個例子脸候,使用in運算符檢查點是否屬于一個矩形。
class Rectangle(val upperLeft: Point, val lowerRight: Point) {
}
operator fun Rectangle.contains(p: Point): Boolean {
//使用Until函數(shù)構建一個開區(qū)間(不包括最后一個元素)绑蔫,檢查坐標是否在這個區(qū)間
return p.x in upperLeft.x until lowerRight.x &&
p.y in upperLeft.y until lowerRight.y
}
val rectangle = Rectangle(Point(10, 20), Point(30, 60))
println(Point(20,40) in rectangle) // true
println(Point(5,10) in rectangle) //false
in右邊的對象會調用contains,in左邊的對象將會作為函數(shù)入?yún)ⅰ?/p>
a in c
//等價于
c.contains(a)
這里值得一提的是运沦,開區(qū)間(Until)是不包括最后一個元素的。閉區(qū)間(10..20)構建的區(qū)間則是包括所有配深。
rangeTo的約定
創(chuàng)建區(qū)間携添,使用..語法,實際上它是rangeTo函數(shù)的約定篓叶。
start..end
//等價于
start.rangeTo(end)
rangTo函數(shù)返回一個區(qū)間烈掠。可以為自己的類定義運算符缸托,但是如果該類實現(xiàn)了Comparable接口左敌,那么就不需要:可以通過kotlin標準庫創(chuàng)建一個任意可以比較元素的區(qū)間。
val now = LocalDate.now()
//創(chuàng)建一個從今天開始到10天的區(qū)間
val vacation = now..now.plusDays(10)
//檢測一個特定日期是否屬于這個區(qū)間
println(now.plusWeeks(1) in vacation) //true
now..now.plusDays(10)會被編譯成now.rangeTo(now.plusDays(10))俐镐。如前所述矫限,rangTo并不是LocalDate的成員函數(shù),而是Comparable的擴展函數(shù)。
rangeTo運算符優(yōu)先級低于算術運算符叼风,建議把參數(shù)括起來避免混淆取董。
val n = 9;
println(0..(n+1)) //0..10
還需要注意的是,0..n.foreach{}不會被編譯无宿,因為必須把區(qū)間表達式括起來才能調用茵汰。
val n = 9;
println((0..n).forEach{ print(it)})//0123456789
在“for”循環(huán)中使用"iterator"的約定
在kotlin中,for循環(huán)中也可以使用in運算符孽鸡,和做區(qū)間檢查一樣经窖。但是這種情況下它的含義不同:它被用來執(zhí)行迭代。即for(x in list){...}被轉換成list.iterator()調用梭灿,然后像在Java一樣画侣,在它上面重復hashNext和Next方法。
這在kotlin中也是一種約定(可以被定義為擴展函數(shù))堡妒。這使得字符串也可以遍歷配乱。
//源碼
public operator fun CharSequence.iterator(): CharIterator = object : CharIterator() {
private var index = 0
public override fun nextChar(): Char = get(index++)
public override fun hasNext(): Boolean = index < length
}
也可以自己定義iterator方法。舉個例子皮迟,遍歷日期搬泥。
operator fun ClosedRange<LocalDate>.iterator(): Iterator<LocalDate> =
//這個對象實現(xiàn)了遍歷LocalDate>元素的iterator
object : Iterator<LocalDate> {
var current = start
override fun hasNext(): Boolean =
//使用了compareTo約定
current <= endInclusive
//修改前返回當前日期作為結果
override fun next(): LocalDate = current.apply {
//日期增加一天
current = plusDays(1) }
}
val newYear = LocalDate.ofYearDay(2018,4)
val daysoff = newYear.minusDays(1)..newYear
for (dayoff in daysoff){
println(dayoff)}
//2018-01-03
//2018-01-04
注意如何自定義區(qū)間類型上定義iterator方法:使用LocalDate作為參數(shù)。rangeTo庫函數(shù)返回一個ClosedRange實例伏尼,并且ClosedRange的擴展iterator擴展允許在for循環(huán)中使用區(qū)間實例忿檩。
結構聲明和組件函數(shù)
解構聲明允許展開單個符合值,并使它來初始化多個單獨變量爆阶。
val p =Point(10,20)
//聲明變量x,y,然后用p組件來初始化
val (x,y) = p
println(x) //10
println(y) //20
一個解構聲明看起來像普通變量聲明燥透,但是它有多個變量。實際上解構聲明也用到了約定原理辨图。要在結構聲明中初始化每個變量班套,將調用componentN函數(shù),其中N是聲明中變量的位置故河。
val (x,y) = p
//等價
val x =p.component1()
val y = p.component2()
數(shù)據(jù)類會自動生成該函數(shù)吱韭,非數(shù)據(jù)類手動聲明。
class Point(val x:Int,val y:Int) {
operator fun component1() = x
operator fun component2() = y
}
解構聲明主要使用場景之一鱼的,是從一個函數(shù)返回多個值理盆。如此一來,可以定義一個數(shù)據(jù)類保存返回所需值凑阶,并將它作為函數(shù)返回類型猿规。調用函數(shù)后,可以使用解構聲明方式晌砾,輕松展開它坎拐,使用其中值。
舉個例子,將文件名分割成名字和擴展名哼勇。
//聲明數(shù)據(jù)類持有值
data class NameComponent (val name:String,val extension:String){
}
fun splitFileName(fullName:String):NameComponent{
val result =fullName.split(".",limit = 2)
return NameComponent(result[0],result[1])
}
val file = "Main.kt"
val (name, ext) = splitFileName(file)
println("name:$name ext:$ext") //name:Main ext:kt
可簡化
fun splitFileName(fullName:String):NameComponent{
val (name,ext) =fullName.split(".",limit = 2)
return NameComponent(name,ext)
}
componentN函數(shù)不可能無限聲明都伪,最多只能訪問前五個元素。讓一個函數(shù)能返回多個值有更簡單辦法积担,使用標準庫中的Pair和Triple類陨晶。
解構聲明和循環(huán)
解構聲明不僅可以用作函數(shù)的頂層語句,還可以用在其它可以聲明變量地方帝璧。如in循環(huán)先誉。
//解構聲明遍歷map
fun printEntries(map:Map<String,String>){
//in循環(huán)中使用解構聲明
for ((key,vaule) in map){
println("$key,$vaule")
}
val map = mapOf("Oracle" to "java", "jb" to "kotlin")
printEntries(map)
//Oracle,java
//jb,kotlin
上述代碼用到kotlin兩個約定(iterator和解構)。實際代碼轉換如下的烁。
for (entry in map){
val key = entry.component1()
val value = entry.component2()
}
這也恰好說明了擴展函數(shù)對約定及其重要褐耳。
重用屬性訪問的邏輯:委托屬性(重點)
委托屬性是kotlin中最獨特和最強大的功能之一。它可以輕松實現(xiàn)這樣的屬性渴庆,它們處理起來比把值存儲在支持字段中更復雜铃芦,卻不用在每個訪問器中都重復這樣的邏輯。
委托屬性依賴于約定功能襟雷。委托是一種設計模式刃滓,操作對象不用自己執(zhí)行,而是把工作委托給另一個輔助對象耸弄。
//委托屬性基本語法
class Test {
var t by Delegate()
}
屬性P將它的訪問邏輯委托給另一個對象:這里是Delegate類的一個新實例咧虎。通過關鍵字by對其后的表達式求值來獲取這個對象,by可以用于任何符合屬性約定規(guī)則的對象计呈。
//翻譯后代碼
class Delegate {
operator fun getValue(v1:Type,v2:KProperty<*>){
}
operator fun setValue(v1:Type,v2:KProperty<*>,v3:Type){
}
}
class Test {
private val delegate = Delegate()
var t:Type
get() = delegate.getValue(....)
set(value) {
....,value
}
}
按照約定砰诵,Delegate類必須具有getValue和setValue(僅適用于可變變量)方法,它既可以是成員函數(shù)震叮,也可以是擴展函數(shù)胧砰。
惰性初始化和"by lazy"
惰性初始化是一種常見的模式,直到在第一次訪問該屬性的時候苇瓣,才根據(jù)需要創(chuàng)建對象的一部分。當初始化過程要消耗大量資源并且在使用對象時并不總是需要數(shù)據(jù)時偿乖,這非常有用击罪。
舉個例子,訪問一個人寫的郵件列表贪薪,郵件存儲在數(shù)據(jù)庫中媳禁,首次訪問才加載并只執(zhí)行一次。
//偽代碼
class Email {
}
//假設去數(shù)據(jù)庫查找郵件
fun loadEmails(person:Person):List<Email>{
println("load emails for ${person.name}")
return listOf()
}
//使用支持屬性來實現(xiàn)惰性初始化
class Person(val name: String) {
//保存數(shù)據(jù)關聯(lián)委托
private var _emails: List<Email>? = null
val emails: List<Email>? = null
get() {
if (_emails==null){
//訪問去加載
_emails= loadEmails(this)
}
//如果加載了就直接返回
return field
}
}
val p = Person("Alc")
p.emails //load emails for Alc
p.emails //只執(zhí)行一次画切,所以無值
支持屬性技術值得熟練掌握竣稽。_emails(可空)用來存儲這個值,另一個emails(非空則用來提供對屬性的讀取訪問。值得注意的是毫别,它的線程并不是安全的娃弓。
標準庫函數(shù)lazy委托則正好能完美解決問題。它反回一個對象具有一個名為getValue且簽名正確的方法岛宦,因此可以與by(如前所說台丛,委托必須要有getVaule())一起創(chuàng)建委托屬性。lazy的參數(shù)是一個lambda砾肺,可以用它初始值挽霉,默認情況下,lazy函數(shù)是線程安全变汪。
class Person(val name: String) {
val emails by lazy { loadEmails(this) }
}
實現(xiàn)委托屬性
直接舉個例子吧侠坎,當對象屬性更改時通知監(jiān)聽器。
//使用propertyChangeSupport
class PropertyChangeAware {
protected val changeSupport =PropertyChangeSupport(this)
fun addPropertyChangeListener(listener:PropertyChangeListener) {
changeSupport.addPropertyChangeListener(listener)
}
fun removePropertyChangeListener(listener: PropertyChangeListener){
changeSupport.removePropertyChangeListener(listener)
}
}
再寫個Person裙盾,定義一個只讀屬性(名字)和兩個可寫屬性(年齡和工資)实胸,當年齡和工資變化,通知監(jiān)聽器闷煤。
class Person(val name: String,age:Int,salary:Int):PropertyChangeAware() {
var age:Int=age
set(newAge){
//field允許訪問屬性背后支持字段
val oldAge=field
field=newAge
//屬性變化通知監(jiān)聽
changeSupport.firePropertyChange("old",oldAge,newAge)
}
var salary:Int =salary
set(newSalary){
val oldSalary = field
field=newSalary
changeSupport.firePropertyChange("salary",oldSalary,newSalary)
}
}
fun main(args: Array<String>) {
val p = Person("Jojo", 24, 2000)
p.addPropertyChangeListener(
PropertyChangeListener { evt ->
println("Property ${evt.propertyName} change" + "from ${evt.oldValue} to ${evt.newValue}")
}
)
//改變年齡
p.age = 25 //Property old changefrom 24 to 25
//改變工資
p.salary = 6000 //Property salary changefrom 2000 to 6000
}
setter里有很多重復代碼童芹,可抽取類優(yōu)化。
class ObservsbleaProperty(val proName: String,
var propValue: Int,
val changeSupport: PropertyChangeSupport) {
fun getValue()=propValue
fun setValue(newValue:Int){
val oldValue=propValue
propValue=newValue
changeSupport.firePropertyChange(proName,oldValue,newValue)
}
}
改造Person
class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {
var _age = ObservsbleaProperty("age", age, changeSupport)
var age: Int
get() = _age.getValue()
set(newAge) {
_age.setValue(newAge)
}
var _salary=ObservsbleaProperty("salary",salary,changeSupport)
var salary: Int
get() = _salary.getValue()
set(value) {_salary.setValue(value)}
}
這仍然有很大的不足鲤拿,比如需要更多的樣板代碼假褪。kotlin的委托屬性則可以避免這些樣板代碼。
進一步優(yōu)化近顷,改造ObservsbleaProperty匹配kotlin約定生音,從而作為屬性委托。
class ObservsbleaProperty(val proName: String,
var propValue: Int,
val changeSupport: PropertyChangeSupport) {
operator fun getValue(p: Person, prop: KProperty<*>) = propValue
operator fun setValue(p: Person, prop: KProperty<*>, newValue: Int) {
val oldValue = propValue
propValue = newValue
changeSupport.firePropertyChange(proName, oldValue, newValue)
}
}
改造后增加兩個參數(shù)窒升,一個是接收實例缀遍,一個是設置或讀取屬性,另一個表示本身饱须。這個類型為 KProperty域醇,值得一提的是 KProperty是kotlin中的反射。
使用改造后的委托屬性綁定更改蓉媳。
class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {
var age:Int by ObservsbleaProperty(age,changeSupport)
var salary:Int by ObservsbleaProperty(salary,changeSupport)
}
Kotlin會自動將委托存儲在隱藏屬性中譬挚,并在訪問或修改是調用委托的getValue,setValue。實際上Kotlin標準庫已經包含了ObservableProperty的類酪呻,不過需要傳遞lambda减宣。
//使用標準庫的函數(shù)來實現(xiàn)屬性修改通知
class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {
var age:Int by ObservsbleaProperty(age,changeSupport)
var salary:Int by ObservsbleaProperty(salary,changeSupport)
}
by右邊的表達式不一定是新創(chuàng)建的實例,也可以是函數(shù)調用玩荠,另一個屬性或任何其它表達式漆腌,只要它的值能被編譯器用正確參數(shù)來調用getValue,setValue就行贼邓。
屬性變換規(guī)則
委托屬性的變換規(guī)則到這里相信已經清楚明了。即在每個屬性訪問器中闷尿,編譯器都會生成對應的getVaule和setVaule塑径。
//get
val x =c.prop
//等價于
val x =<delegate>.getValue(c,<property>)
//set
c.prop =x
//等價于
val x =<delegate>.setValue(c,<property>,x)
這個機制能適應很多場景。比如在map中保存屬性值悠砚。
class Person {
private val map = hashMapOf<String,String>()
fun setMap(key:String,value:String){
map[key]=value
}
val keyName:String by map
}
fun main(args: Array<String>) {
val p = Person()
val map = mapOf("keyName" to "JoJo","jb" to "kotlin")
for ((key,value) in map){
p.setMap(key,value)
}
//如果沒有會拋異常
println(p.keyName) //JoJo
}
因為標準庫已經在Map和MutableMap接口上定義了getValue,setValue的擴展函數(shù)晓勇。所以這里可以直接使用。
總結灌旧,略绑咱。