kotlin運算符重載及約定

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ù)晓勇。所以這里可以直接使用。

總結灌旧,略绑咱。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市枢泰,隨后出現(xiàn)的幾起案子描融,更是在濱河造成了極大的恐慌,老刑警劉巖衡蚂,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窿克,死亡現(xiàn)場離奇詭異,居然都是意外死亡毛甲,警方通過查閱死者的電腦和手機年叮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來玻募,“玉大人只损,你說我怎么就攤上這事∑哌郑” “怎么了跃惫?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵压彭,是天一觀的道長展蒂。 經常有香客問我俺叭,道長纵菌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任贫母,我火速辦了婚禮洞就,結果婚禮上卵皂,老公的妹妹穿的比我還像新娘悼粮。我一直安慰自己拇泣,他們只是感情好,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布矮锈。 她就那樣靜靜地躺著,像睡著了一般睁蕾。 火紅的嫁衣襯著肌膚如雪苞笨。 梳的紋絲不亂的頭發(fā)上债朵,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音瀑凝,去河邊找鬼序芦。 笑死,一個胖子當著我的面吹牛粤咪,可吹牛的內容都是我干的谚中。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼寥枝,長吁一口氣:“原來是場噩夢啊……” “哼宪塔!你這毒婦竟也來了?” 一聲冷哼從身側響起囊拜,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤某筐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后冠跷,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體南誊,經...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年蜜托,在試婚紗的時候發(fā)現(xiàn)自己被綠了抄囚。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡橄务,死狀恐怖幔托,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情仪糖,我是刑警寧澤柑司,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站锅劝,受9級特大地震影響攒驰,放射性物質發(fā)生泄漏。R本人自食惡果不足惜故爵,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一玻粪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧诬垂,春花似錦劲室、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至隧枫,卻和暖如春喉磁,著一層夾襖步出監(jiān)牢的瞬間谓苟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工协怒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留涝焙,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓孕暇,卻偏偏與公主長得像仑撞,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子妖滔,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內容