20170615_kotlin入門_part2

part1看完了HelloWorld和BasicSyntax咱們接著往后面看(依然是跟著Try Kotlin進行~)

Destructuring declarations and Data classes

基本語法的demo看完就迎接更有意思的東西了,構造聲明和數(shù)!據(jù)!類!

Destructuring declarations

/**
 * This example introduces a concept that we call destructuring declarations.
 * It creates multiple variable at once. Anything can be on the right-hand
 * side of a destructuring declaration, as long as the required number of component
 * functions can be called on it.
 * See http://kotlinlang.org/docs/reference/multi-declarations.html#multi-declarations
 */

fun main(args: Array<String>) {
    val pair = Pair(1, "one")

    val (num, name) = pair

    println("num = $num, name = $name")
}

class Pair<K, V>(val first: K, val second: V) {
    operator fun component1(): K {
        return first
    }

    operator fun component2(): V {
        return second
    }
}

這個小例子介紹了一個叫做"解構聲明"的概念,它是在干嘛呢?它呀,能一次創(chuàng)建多個變量;

任何對象都能放在一個解構聲明的右邊,只要可以調(diào)用到所需數(shù)量的組件函數(shù)即可.

例如例子中的Pair類,類中有兩個componentX function,X對應的就是解構創(chuàng)建變量的位置

在kotlin中,data class是隱含component function的,例如:

fun main(args: Array<String>) {
    val person1 = Person1("zhangsan", 3)
    val (name1, age1) = person1
    println("name1 =$name1 and age = $age1")

}

data class Person1(val name: String, val age: Int)

控制臺輸出:name1 =zhangsan and age = 3

如果想自己的類也可以使用解構聲明,那就需要寫出componentX function,例如:

fun main(args: Array<String>) {
    val person2 = Person2("lisi", 4)
    val (name2, age2) = person2

    println("name1 = $name2 and age1 = $age2")

}


class Person2(val name: String, val age: Int) {
    operator fun component2(): String {
        return name
    }

    operator fun component1(): Int {
        return age
    }
}

//
控制臺輸出:name1 = 4 and age1 = lisi

可以看到componentX中的X的的確確就是解構時返回值的角標

Data classes

/**
 *  Data class gets component functions, one for each property declared
 *  in the primary constructor, generated automatically, same for all the
 *  other goodies common for data: toString(), equals(), hashCode() and copy().
 *  See http://kotlinlang.org/docs/reference/data-classes.html#data-classes
 */

data class User(val name: String, val id: Int)

fun getUser(): User {
    return User("Alex", 1)
}

fun main(args: Array<String>) {
    val user = getUser()
    println("name = ${user.name}, id = ${user.id}")

    // or

    val (name, id) = getUser()
    println("name = $name, id = $id")

    // or

    println("name = ${getUser().component1()}, id = ${getUser().component2()}")
}

data class會根據(jù)主構造函數(shù)的參數(shù)自動生成component functions,同樣的也會生成諸如:toString(),equals(),hashCode(),copy()等針對數(shù)據(jù)的常用函數(shù)

上面的三個pirntln會輸出同樣的結果:name = Alex, id = 1

Traversing a map

/**
 *  Kotlin Standard Library provide component functions for Map.Entry
 */

fun main(args: Array<String>) {
    val map = hashMapOf<String, Int>()
    map.put("one", 1)
    map.put("two", 2)

    for ((key, value) in map) {
        println("key = $key, value = $value")
    }
}

如果想遍歷一個map,kotlin的標準庫已經(jīng)給Map.Entry提供了component function,我們就可以直接使用解構函數(shù)輸出它啦!

Autogenerated functions

/**
 * Data class gets next functions, generated automatically:
 * component functions, toString(), equals(), hashCode() and copy().
 * See http://kotlinlang.org/docs/reference/data-classes.html#data-classes
 */

data class User(val name: String, val id: Int)

fun main(args: Array<String>) {
    val user = User("Alex", 1)
    println(user) // toString()

    val secondUser = User("Alex", 1)
    val thirdUser = User("Max", 2)

    println("user == secondUser: ${user == secondUser}")
    println("user == thirdUser: ${user == thirdUser}")

    // copy() function
    println(user.copy())
    println(user.copy("Max"))
    println(user.copy(id = 2))
    println(user.copy("Max", 2))
}

data class會自動成component functions, toString(), equals(), hashCode() , copy().這個我已經(jīng)在上面提到,就不贅述了

建議大家敲一手兩手看看結果感受感受(這里的copy函數(shù)能自動識別函數(shù)來進行copy,我覺得很神奇,看.class文件也沒出現(xiàn)copy函數(shù),不知道怎么實現(xiàn)的TODO)0

Delegated properties

Custom delegate

/**
 * There's some new syntax: you can say `val 'property name': 'Type' by 'expression'`.
 * The expression after by is the delegate, because get() and set() methods
 * corresponding to the property will be delegated to it.
 * Property delegates don't have to implement any interface, but they have
 * to provide methods named getValue() and setValue() to be called.</p>
 */

import kotlin.reflect.KProperty

class Example {
    //聲明一個成員變量,委派給了Delegate()
    var p: String by Delegate()

    //重寫toString,返回值為"Example Class"
    override fun toString() = "Example Class"
}

class Delegate {
    operator fun getValue(thisRef: Any?, prop: KProperty<*>): String {
        return "$thisRef, thank you for delegating '${prop.name}' to me!"
    }

    operator fun setValue(thisRef: Any?, prop: KProperty<*>, value: String) {
        println("$value has been assigned to ${prop.name} in $thisRef")
    }
}

fun main(args: Array<String>) {
    val e = Example()
    /**
    打印e.p,輸出結果是"Example Class, thank you for delegating 'p' to me!"
    即調(diào)用toString的時候?qū)oString返回的字符串放到了Delegate() 的 getValue()的第一個參數(shù)中,然后返回的是getValue的值
     */
    println(e.p)

    /**
     * 給p賦值的時候調(diào)用了Delegate()的setValue(),會執(zhí)行setValue()函數(shù)中的打印語句,輸出:"NEW has been assigned to p in Example Class"
     */
    e.p = "NEW"
}

kotlin中有像 val 'property name': 'Type' by 'expression' 這樣的新語法 ,冒號后面如果是表達式,那這就被稱作委派,因為關聯(lián)到該屬性的get()/set()函數(shù)會被委派出去;屬性委派是不需要實現(xiàn)任何接口的,但他們需要被提供名為getValue()和setValue()的函數(shù)

基本用法是這樣,還想不到怎么運用起來,畢竟入門嘛,先把概念稍微理清再說

Lazy property

/**
 * Delegates.lazy() is a function that returns a delegate that implements a lazy property:
 * the first call to get() executes the lambda expression passed to lazy() as an argument
 * and remembers the result, subsequent calls to get() simply return the remembered result.
 * If you want thread safety, use blockingLazy() instead: it guarantees that the values will
 * be computed only in one thread, and that all threads will see the same value.
 */

class LazySample {
    val lazy: String by lazy {
        println("computed!")
        "my lazy"
    }
}

fun main(args: Array<String>) {
    val sample = LazySample()
    println("lazy = ${sample.lazy}")
    println("lazy = ${sample.lazy}")
}

Delegates.lazy()是一個返回實現(xiàn)延遲屬性的委托的函數(shù):

第一次調(diào)用get()函數(shù)時執(zhí)行的lambda表達式會傳一個參數(shù)給lazy()函數(shù),然后lazy()會記住這個結果,后面再調(diào)用get(),就會很輕松的得到第一次調(diào)用記錄的結果.

注釋里關于線程安全的內(nèi)容好像已經(jīng)過時了,找了個官方說法是:默認的lazy就是線程安全的,級別是LazyThreadSafetyMode.SYNCHRONIZED,它會確保如果我們的屬性是第一次初始化,那它只會在單一線程進行;

如果我們已經(jīng)確保線程是安全的,不需要Delegates去處理線程安全問題,那么可以對lazy函數(shù)更改線程安全級別為LazyThreadSafetyMode.NONE,例如下面的

 val lazy: String by lazy(LazyThreadSafetyMode.NONE) {
        println("computed!")
        "my lazy"
    }

除了還有一個級別是LazyThreadSafetyMode.PUBLICATION:如果多個線程同時進行初始化,那么lazy()會將第一個返回的結果存起來,以后拿來用

Observable property

/**
 * The observable() function takes two arguments: initial value and a handler for modifications.
 * The handler gets called every time we assign to `name`, it has three parameters:
 * a property being assigned to, the old value and the new one. If you want to be able to veto
 * the assignment, use vetoable() instead of observable().
 */
import kotlin.properties.Delegates

class User {
    var name: String by Delegates.observable("no name") {
        d, old, new ->
        println("$old - $new")
    }
}

fun main(args: Array<String>) {
    val user = User()
    user.name = "Carl"
}

observable()需要兩個參數(shù):初始值和一個處理改變的handler.

這個handler能在我們每次給name賦值的時候調(diào)用,它有三個參數(shù):①被賦值的參數(shù)②它的舊值③它的新值

如果你想否決(取消?)這次賦值,用vetoable()代替observable()

注釋看完沒太明白vetoable()是在干嘛,那就寫兩句結合著輸出理解吧

class Child {
    
  /**
  vetoable方法里的參數(shù)是初始值,后面表達式中的desc是該參數(shù),old是舊值,new是新值;該表達式需要返回一個boolean;
  這個boolean值為true時才能對該屬性賦值,如果為false則不賦值,詳細可見下面的例子  
  */
    var height:Int by Delegates.vetoable(100){ desc, old, new ->
        new>old
    }

}

fun main(args: Array<String>) {
    val child=Child()
    child.height=90
    println("小孩的身高,第一次:${child.height}")//小孩初始身高是100,90>100? ->false,所以不賦值

    child.height=110
    println("小孩的身高,第二次:${child.height}")//小孩當前身高還是100,110>100? ->true,所以賦值,下面兩個以此類推

    child.height=130
    println("小孩的身高,第三次:${child.height}")

    child.height=80
    println("小孩的身高,第四次:${child.height}")

}

輸出的結果:

小孩的身高,第一次:100
小孩的身高,第二次:110
小孩的身高,第三次:130
小孩的身高,第四次:130

所以委派的vetoable()的使用大概就是這么回事啦

NotNull property

/**
 * Users frequently ask what to do when you have a non-null var, but you don't have an
 * appropriate value to assign to it in constructor (i.e. it must be assigned later)?
 * You can't have an uninitialized non-abstract property in Kotlin. You could initialize it
 * with null, but then you'd have to check every time you access it. Now you have a delegate
 * to handle this. If you read from this property before writing to it, it throws an exception,
 * after the first assignment it works as expected.
 */

import kotlin.properties.Delegates

class User {
    var name: String by Delegates.notNull()

    fun init(name: String) {
        this.name = name
    }
}

fun main(args: Array<String>) {
    val user = User()
    // user.name -> IllegalStateException
    user.init("Carl")
    println(user.name)
}

咱們在Kotlin中不能有未初始化的非抽象屬性,我們可以將其初始化為null,但這之后當我們需要用它時,我們每次都要檢查它,我們現(xiàn)在可以通過委托來處理它;

當咱們在它沒賦值的時候訪問它,就會拋一個異常,如果是賦值后再訪問,他就能像我們期望的那樣工作啦

notNull()委托也是針對于Kotlin的np-check而出現(xiàn)的,有了這個委托就不用每次進行一個非null判斷,當然也可以通過聲明該變量肯定不為null的方式來處理(在一個變量后加兩個感嘆號),就看情況選擇了

Properties in map

/**
 * Properties stored in a map. This comes up a lot in applications like parsing JSON
 * or doing other "dynamic" stuff. Delegates take values from this map (by the string keys -
 * names of properties). Of course, you can have var's as well,
 * that will modify the map upon assignment (note that you'd need MutableMap instead of read-only Map).
 */

class User(val map: Map<String, Any?>) {
    val name: String by map
    val age: Int     by map
}

fun main(args: Array<String>) {
    val user = User(mapOf(
            "name" to "John Doe",
            "age"  to 25
    ))

    println("name = ${user.name}, age = ${user.age}")
}

參數(shù)存儲在map中這種情況有很多,例如解析JSON或者別的動態(tài)玩意兒,委托能從這些map中獲得值.

你當然也可以在map里存取變量,這會在分配變量時更改這個map(所以記住如果需要在map里存取變量,得使用一個可變的Map)

(這個章節(jié)的知識我想不到使用場景)

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子罩锐,更是在濱河造成了極大的恐慌,老刑警劉巖吠撮,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辉浦,死亡現(xiàn)場離奇詭異,居然都是意外死亡氮昧,警方通過查閱死者的電腦和手機赖阻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門蝶押,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人火欧,你說我怎么就攤上這事棋电。” “怎么了苇侵?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵赶盔,是天一觀的道長。 經(jīng)常有香客問我衅檀,道長招刨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任哀军,我火速辦了婚禮沉眶,結果婚禮上,老公的妹妹穿的比我還像新娘杉适。我一直安慰自己谎倔,他們只是感情好,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布猿推。 她就那樣靜靜地躺著片习,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蹬叭。 梳的紋絲不亂的頭發(fā)上藕咏,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機與錄音秽五,去河邊找鬼孽查。 笑死,一個胖子當著我的面吹牛坦喘,可吹牛的內(nèi)容都是我干的盲再。 我是一名探鬼主播西设,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼答朋!你這毒婦竟也來了贷揽?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤梦碗,失蹤者是張志新(化名)和其女友劉穎禽绪,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叉弦,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡丐一,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了淹冰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡巨柒,死狀恐怖樱拴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情洋满,我是刑警寧澤晶乔,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站牺勾,受9級特大地震影響正罢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜驻民,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一翻具、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧回还,春花似錦裆泳、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蝗柔,卻和暖如春闻葵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背癣丧。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工槽畔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人坎缭。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓竟痰,卻偏偏與公主長得像签钩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子坏快,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容