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é)的知識我想不到使用場景)