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) {
    val pair = Pair(1, "one")

    val (num, name) = pair

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

class Pair(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) {
    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) {
    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) {
    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) {
    val map = hashMapOf()
    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) {
    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.

*/ 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) { 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) {
    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) {
    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) {
    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) {
    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) {
    val name: String by map
    val age: Int     by map
}

fun main(args: Array) {
    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)

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

你可能感兴趣的:(20170615_kotlin入门_part2)