20170615_kotlin入门_part2

part1看完了HelloWorld和BasicSyntax咱们接着往后面看(依然是跟着Try Kotlin进行~)

Destructuring declarations and Data classes

基本语法的demo看完就迎接更有意思的东西了,构造声明和数!据!类!

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
    }
}

这个小例子介绍了一个叫做"解构声明"的概念,它是在干嘛呢?它呀,能一次创建多个变量;

任何对象都能放在一个解构声明的右边,只要可以调用到所需数量的组件函数即可.

例如例子中的Pair类,类中有两个componentX function,X对应的就是解构创建变量的位置

在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会根据主构造函数的参数自动生成component functions,同样的也会生成诸如:toString(),equals(),hashCode(),copy()等针对数据的常用函数

上面的三个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的标准库已经给Map.Entry提供了component function,我们就可以直接使用解构函数输出它啦!

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().这个我已经在上面提到,就不赘述了

建议大家敲一手两手看看结果感受感受(这里的copy函数能自动识别函数来进行copy,我觉得很神奇,看.class文件也没出现copy函数,不知道怎么实现的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!"
    即调用toString的时候将toString返回的字符串放到了Delegate() 的 getValue()的第一个参数中,然后返回的是getValue的值
     */
    println(e.p)

    /**
     * 给p赋值的时候调用了Delegate()的setValue(),会执行setValue()函数中的打印语句,输出:"NEW has been assigned to p in Example Class"
     */
    e.p = "NEW"
}

kotlin中有像 val 'property name': 'Type' by 'expression' 这样的新语法 ,冒号后面如果是表达式,那这就被称作委派,因为关联到该属性的get()/set()函数会被委派出去;属性委派是不需要实现任何接口的,但他们需要被提供名为getValue()和setValue()的函数

基本用法是这样,还想不到怎么运用起来,毕竟入门嘛,先把概念稍微理清再说

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()是一个返回实现延迟属性的委托的函数:

第一次调用get()函数时执行的lambda表达式会传一个参数给lazy()函数,然后lazy()会记住这个结果,后面再调用get(),就会很轻松的得到第一次调用记录的结果.

注释里关于线程安全的内容好像已经过时了,找了个官方说法是:默认的lazy就是线程安全的,级别是LazyThreadSafetyMode.SYNCHRONIZED,它会确保如果我们的属性是第一次初始化,那它只会在单一线程进行;

如果我们已经确保线程是安全的,不需要Delegates去处理线程安全问题,那么可以对lazy函数更改线程安全级别为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()需要两个参数:初始值和一个处理改变的handler.

这个handler能在我们每次给name赋值的时候调用,它有三个参数:①被赋值的参数②它的旧值③它的新值

如果你想否决(取消?)这次赋值,用vetoable()代替observable()

注释看完没太明白vetoable()是在干嘛,那就写两句结合着输出理解吧

class Child {
    
  /**
  vetoable方法里的参数是初始值,后面表达式中的desc是该参数,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,但这之后当我们需要用它时,我们每次都要检查它,我们现在可以通过委托来处理它;

当咱们在它没赋值的时候访问它,就会抛一个异常,如果是赋值后再访问,他就能像我们期望的那样工作啦

notNull()委托也是针对于Kotlin的np-check而出现的,有了这个委托就不用每次进行一个非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}")
}

参数存储在map中这种情况有很多,例如解析JSON或者别的动态玩意儿,委托能从这些map中获得值.

你当然也可以在map里存取变量,这会在分配变量时更改这个map(所以记住如果需要在map里存取变量,得使用一个可变的Map)

(这个章节的知识我想不到使用场景)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容