1、 componentX (多聲明)
val f1 = Forecast(Date(), 27.5f, "Shinny")
val (date, temperature, details) = f1
//=======================
// 上面的多聲明會(huì)被編譯成下面的代碼
val date = f1.component1()
val temperature = f1.component2()
val details = f1.copmponent3()
// 映射對(duì)象的每一個(gè)屬性到一個(gè)變量中,這就是 多聲明。
// object class 默認(rèn)具有該屬性。但普通 class 想要具有這種屬性,需要這樣做:
class person(val name: String, val age: Int) {
operator fun component1(): String {
return name
}
operator fun component2(): Int {
return age
}
}
val 必須有: 用來保存在 component1 和 component2 中返回構(gòu)造函數(shù)傳進(jìn)來的參數(shù)的。
operator 暫時(shí)還不明真相,IDE 提示的。 操作符重載,函數(shù)名為操作符名(即系統(tǒng)默認(rèn)的關(guān)鍵詞,此處為 component1,component2).當(dāng)使用該操作時(shí),自己重寫的操作會(huì)覆蓋系統(tǒng)默認(rèn)的操作。
// 常見用法:該特性功能強(qiáng)大,可以極大的簡(jiǎn)化代碼量。 如 map 中的擴(kuò)展函數(shù)實(shí)現(xiàn),允許在迭代時(shí)使用 key value
for ((key, value) in map) {
Log.d("map","key:$key, value:$value")
}
2、 Companion objects (伴生對(duì)象)
類似于 java 中的 靜態(tài)屬性或方法,可以表示一個(gè)類中的靜態(tài)屬性、常量、函數(shù)。
3、with function
> 包含在 kotlin 的標(biāo)準(zhǔn)函數(shù)庫中。接收一個(gè)對(duì)象和一個(gè)擴(kuò)展函數(shù)作為它的參數(shù),然后使這個(gè)對(duì)象擴(kuò)展這個(gè)函數(shù)。表示所有在括號(hào)中編寫的代碼都是作為對(duì)象(第一個(gè)參數(shù))的一個(gè)擴(kuò)展函數(shù),可以就像 this 一樣使用所有它的 public 方法和屬性。當(dāng)針對(duì)同一個(gè)對(duì)象做很多操作時(shí)非常有利于簡(jiǎn)化代碼。
4、 operator 操作符重載
> Kotlin 有一些固定數(shù)量象征性的操作符,可以在任何類中很容易地使用。方法是創(chuàng)建一個(gè)方法,方法名為保留的操作符關(guān)鍵字,這樣就可以讓這個(gè)操作符的行為映射到這個(gè)方法。
一元操作符
| 操作符 | 函數(shù) |
|---|---|
| +a | a.unaryPlus() |
| -a | a.unaryMinus() |
| !a | a.not() |
| a++ | a.inc() |
| a-- | a.dec() |
二元操作符
| 操作符 | 函數(shù) |
|---|---|
| a + b | a.plus(b) |
| a - b | a.minus(b) |
| a * b | a.times(b) |
| a / b | a.div(b) |
| a % b | a.mod(b) |
| a..b | a.rangeTo(b) |
| a in b | a.contains(b) |
| a !In b | !a.contains(b) |
| a += b | a.plusAssign(b) |
| a -= b | a.minusAssign(b) |
| a *= b | a.timesAssign(b) |
| a /= b | a.divAssign(b) |
| a %= b | a.modAssign(b) |
數(shù)組操作符
| 操作符 | 函數(shù) |
|---|---|
| a[i] | a.get(i) |
| a[i,j] | a.get(i,j) |
| a[i_1,...,i_n] | a.get(i_1,...,i_n) |
| a[i] = b | a.set(i,b) |
| a[i,j] = b | a.set(i,j,b) |
| a[i_1,...,i_n] = b | a.set(i_1,...,i_n,b) |
等于操作符
| 操作符 | 函數(shù) |
|---|---|
| a == b | a?.equals(b)?:b === null |
| a != b | !(a?.equals(b)?: b === null) |
函數(shù)調(diào)用操作符
| 操作符 | 函數(shù) |
|---|---|
| a(i) | a.invoke(i) |
| a(i,j) | a.invoke(i,j) |
| a(i_1,...,i_n) | a.invoke(i_1,...,i_n) |
3、 lambda
函數(shù)里定義 lambda 表達(dá)式形參
fun setOnClickListener(listener: (View) -> Unit)
// 表達(dá)式通過參數(shù)的形式被定義在箭頭的左邊(被圓括號(hào)包圍),然后在箭頭的右邊返回結(jié)果。該例中,接收一個(gè) View, 返回 Unit
調(diào)用
view.setOnClickListener({ toast("clicked")})
// 當(dāng)定義了一個(gè)方法,必須使用大括號(hào)包圍,然后在箭頭的左邊指定參數(shù),在箭頭的右邊返回函數(shù)執(zhí)行的結(jié)果。如果左邊的參數(shù)沒有使用到,可以省略左邊的參數(shù):
view.setOnClickListener({ toast("clicked")})
//如果這個(gè)函數(shù)的最后一個(gè)參數(shù)是一個(gè)函數(shù),可以把這個(gè)函數(shù)移動(dòng)到圓括號(hào)外:
view.setOnClickListener() { toast("clicked")}
// 最后,如果這個(gè)函數(shù)只有一個(gè)參數(shù),可以省略這個(gè)圓括號(hào)
view.setOnClickListener { toast("clicked")}
4、 inline (內(nèi)聯(lián)函數(shù))
內(nèi)聯(lián)函數(shù)與普通的函數(shù)有點(diǎn)不同。一個(gè)內(nèi)聯(lián)函數(shù)會(huì)在編譯的時(shí)候被替換掉,而不是真正的方法調(diào)用。這在譯寫情況下可以減少內(nèi)存分配和運(yùn)行時(shí)開銷。例如,有一函數(shù)只接收一個(gè)函數(shù)作為它的參數(shù)。如果是普通函數(shù),內(nèi)部會(huì)創(chuàng)建一個(gè)含有那個(gè)函數(shù)的對(duì)象。而內(nèi)聯(lián)函數(shù)會(huì)把我們調(diào)用這個(gè)函數(shù)的地方替換掉,所以它不需要為此生成一個(gè)內(nèi)部的對(duì)象。
// 例一、創(chuàng)建代碼塊只提供 Lollipop 或更高版本來執(zhí)行
inline fun supportsLollipop(code: () -> Unit) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
code()
}
}
// usage
supportsLollipop {
window.setStatusBarColor(Color.BLACK)
}
5、可見性修飾符
1、private
2、protected:一個(gè)包成員不能被定義為 protected.
3、internal:如果一個(gè)定義為 internal 的包成員的話,對(duì)所在的整個(gè) module 可見。如果它是一個(gè)其他領(lǐng)域的成員,它就需要依賴那個(gè)領(lǐng)域的可見性。如有一個(gè) private 類,那么它的 internal 修飾的函數(shù)的可見性會(huì)限制與它所在的該類的可見性??梢栽L問同一個(gè) module 中的 internal 修飾的類,但不能訪問其他 module的。
4、public: 僅受限于它的領(lǐng)域。一個(gè)定義為 public 的成員被包含在一個(gè) privaet 修飾的勒種,這個(gè)成員在這個(gè)類之外也是不可見的。
6、構(gòu)造器
所有構(gòu)造器默認(rèn)都是 public 的,它們類是可見的,可以被其他地方使用。也可以使用該語法把構(gòu)造函數(shù)修改為 private:
class C private constructor(a: Int) {...}
7、Kotlin for Android Extensions
7.1、Activities / Fragments 的 Android Extensions
import kotlinx.android.synthetic.activity_main.*
// 在 setContentView 被調(diào)用后就可以通過 id 名來訪問 XML 中定義的 View
import kotlinx.android.synthetic.activity_main.*
// 可以通過 include 標(biāo)簽在 activity 默認(rèn)布局中增加內(nèi)嵌的布局。
7.2、Views Android Extensions
import kotlinx.android.synthetic.view_item.view.*
// 綁定一個(gè) xml 中的 view 到另外一個(gè) view。
8、Application 單例化和屬性的 Delegated (by)
class App : Application() {
companion object {
private var instance: Application? = null
fun instance() = instance!!
}
override fun onCreate() {
super.onCreate()
instance = this
}
}
我們可能需要一個(gè)屬性具有一些相同的行為,使用 lazy 或 observable 可以被很有趣的實(shí)現(xiàn)重用,而不是一次又一次的去聲明那些相同的代碼。kotlin 提供了一個(gè)委托屬性到一個(gè)類的方法。這就是委托屬性
class Delegate<T> : ReadWriteProperty<Any?, T> {
fun getValue(thisRef: Any?, property: KProperty<*>): T {
return ...
}
fun setValue(thisRef: Any?,property: KProperty<*>, value: T) {...}
// 如果該屬性是不可修改(val), 就會(huì)只有一個(gè) getValue 函數(shù)
}
8.1、lazy
1、包含一個(gè) lambda,當(dāng)?shù)谝淮螆?zhí)行 getValue 時(shí)該 lambda 會(huì)被調(diào)用,所以該屬性可以被延遲初始化。之后的調(diào)用都只會(huì)返回同一個(gè)值。
2、lazy 操作符是線程安全的。
3、如果不擔(dān)心多線程問題或想提高更多的性能,可以使用 lazy(LazyThreadSafeMode.NONE) { ... }
4、一般 lazy 委托的代碼塊可以阻止在多個(gè)不同的線程中創(chuàng)建多個(gè)對(duì)象。
class App : Application() {
val database: SQLiteOpenHelper by lazy {
MyDatabaseHelper(applicationContext)
}
override fun onCreate() {
super.onCreate()
val db = database.writeableDatabase
}
}
8.2、Observable
1、該委托可以檢測(cè)希望觀察的屬性變化。當(dāng)被觀察屬性的 set 方法被調(diào)用時(shí),它就會(huì)自動(dòng)執(zhí)行我們指定的 lambda 表達(dá)式。所以一旦該屬性被賦予了新值,則可以收到被委托的屬性、舊值和新值。
class ViewModel(val db: MyDatabase) {
var myProperty by Delegates.observable("") {
d,old,new ->
db.saveChanges(this,new)
}
}
8.3、Vetoable
1、一個(gè)特殊的 observable, 可以來決定是否保存這個(gè)值。在真正保存之前進(jìn)行一些條件判斷。
var positiveNumber = Delegates.vetoable(0) {
d, old, new ->
new >= 0
}
// 上面這個(gè)委托只允許在新的值是正數(shù)時(shí)執(zhí)行保存。在 lambda 中,最后一行表示返回值。不需要使用 return 關(guān)鍵字(實(shí)質(zhì)上不能被編譯)
8.4、Not Null
1、場(chǎng)景1:需要在某些地方初始化該屬性,但不能在構(gòu)造函數(shù)中確定,或不能在構(gòu)造函數(shù)中做任何事。
2、場(chǎng)景2:在 Activity fragment service receivers...中,一個(gè)非抽象的屬性在構(gòu)造函數(shù)執(zhí)行之前需要被賦值。
3、解決方案1:使用可 null 類型并且賦值為 null,直到真正去賦值。氮素,在使用時(shí)就需要不停的進(jìn)行 not null 判斷。
4、解決方案2:使用 notnull 委托。含有一個(gè)可 null 的變量并會(huì)在設(shè)置該屬性時(shí)分配一個(gè)真實(shí)的值。如果該值在被獲取之前沒有被分配,它就會(huì)拋出一個(gè)異常。
class App : Application() {
companion object {
var instance: App by Delegates.notnull()
}
override fun onCreate() {
super.onCreate()
instance = this
}
}
8.5、從 Map 中映射值
另一種委托方式,屬性的值會(huì)從一個(gè)map中獲取 value,屬性的名字對(duì)應(yīng)這個(gè)map 中的 key。
import kotlin.properties.getValue
class Configuration(map: Map<String,Any?>) {
val width: Int by map
val height: Int by map
val dp: Int by map
val deviceName: String by map
}
// usage
conf = Configuration(mapof(
"width" to 1080,
"height" to 720,
"dp" to 240,
"deviceName" to "myDecive"
))
8.6 custom delegate
自定義委托需要實(shí)現(xiàn) ReadOonlyProperty / ReadWriteProperty 兩個(gè)類,具體取決于被委托的對(duì)象是 val 還是 var
// step1
private class NotNullSingleValueVar<T>() : ReadWriteProperty<Any?, T> {
private var value: T? = null
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value ?: throw IllegalStateException("${desc.name not initialized}")
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = if (this.value == null) value else throw IllegalStateException("${desc.name} already initialized")
}
}
// step2: usage
object DelegatesExt {
fun notNullSingleValue<T>(): ReadWriteProperty<Any?, T> = NotNullSingleValueVar()
}
8.7 重新實(shí)現(xiàn) Application 單例
class App : Application() {
companion object {
var instance: App by Delegates.notNull()
}
override fun onCreate() {
super.onCreate()
instance = this
}
}
// 此時(shí)可以在 app 的任何地方修改這個(gè)值,因?yàn)?*如果使用 Delegates.notNull(), 屬性必須是 var 的??梢允褂脛倓倓?chuàng)建的委托,只能修改該值一次
companion object {
var instance: App by DeleagesExt.notNullSingleValue()
}
9、集合和函數(shù)操作
1、Iterable: 父類??梢员闅v一系列的都是實(shí)現(xiàn)這個(gè)接口
2、MutableIterable: 一個(gè)支持便利的同時(shí)可以執(zhí)行刪除的 Iterables
3、Collection:
4、MutableCollection:支持增加刪除item 的 collection。提供了額外的函數(shù),如 add、remove、clear等
5、List: 范性有序集合。
6、MutableList: 支持增刪item 的 List
7、Set: 無序并不支持重復(fù) item 的 集合
8、MutableSet: 支持增刪item 的 Set
9、 Map:
10、MutableMap: 支持增刪 item 的 map
9.1、總數(shù)操作符
1、Any:如果至少有一個(gè)元素符合給出的判斷條件,則返回 true
val list = listOf(1,2,3,4,5,6)
assertTrue(list.any { it % 2 == 0 })
assertFalse(list.any { it > 10})
2、all:如果全部的元素符合給出的判斷條件,則返回 true
assertTrue(list.add { it < 10})
assertFalse(list.all { it % 2 == 0})
3、count: 返回符合給出判斷條件的元素總數(shù)
assertEquals(3,list.count {it % 2 == 0})
4、fold: 在一個(gè)初始值的基礎(chǔ)上從第一項(xiàng)到最后一項(xiàng)通過一個(gè)函數(shù)累計(jì)所有的元素
asserEquals(25, list.fold(4) { total, next -> total + next})
5、foldRight: 與 fold 一樣,但順序是從最后一項(xiàng)到第一項(xiàng)。
6、forEach: 遍歷所有元素,并執(zhí)行給定的操作。
list.forEach { println(it) }
7、forEachIndexed: 與 forEach ,同時(shí)可得到元素的 Index
list.forEachIndexed { index, value -> println("position $index contains a $value")}
8、max: 返回最大一項(xiàng),如果沒有則返回 null
9、maxBy: 根據(jù)給定的函數(shù)返回最大的一項(xiàng),沒有返回 null
assertEquals(1, list.maxBy { -it })
10、min
11、minBy
12、none
13、reduce:與fold一樣,但沒有初始值。通過一個(gè)函數(shù)從第一項(xiàng)到最后一項(xiàng)進(jìn)行累計(jì)。
assertEquals(21, list.reduce {total, next -> total + next})
14、reduceRight: 順序從最后一項(xiàng)到第一項(xiàng)
15、sumBy: 返回所有每一項(xiàng)通過函數(shù)轉(zhuǎn)換之后的數(shù)據(jù)的總和。
9.2、過濾操作符
1、drop:返回包含去掉前 n 個(gè)元素的所有元素的列表
assertEquals(listOf(5,6), list.drop(4))
2、dropWhile: 返回根據(jù)給定函數(shù)從第一項(xiàng)開始去掉指定元素的列表
3、dropLastWhile:返回根據(jù)給定函數(shù)從最后一項(xiàng)開始去掉指定元素的列表
4、filter:過濾
assertEquals(listOf(2,4,6), list.filter{it % 2 == 0})
5、filterNot
6、filterNotNull
7、slice:過濾一個(gè)list 中指定 index 的元素
8、take: 返回從第一個(gè)開始的 n 個(gè)元素
9、takeLast: 返回從最后一個(gè)開始的 n 個(gè)元素
10、takeWhile: 返回從第一個(gè)開始符合給定函數(shù)條件的元素
9.3、映射操作符
1、flatMap:遍歷所有的元素,為每一個(gè)創(chuàng)建一個(gè)集合,最后把所有集合放在一個(gè)集合中。
2、groupBy:返回一個(gè)根據(jù)給定函數(shù)分組后的 map
3、map:返回一個(gè)每一個(gè)元素根據(jù)給定函數(shù)轉(zhuǎn)換所組成的list
4、mapIndexed:返回一個(gè)每一個(gè)元素根據(jù)給定的包含元素 index 的函數(shù)轉(zhuǎn)換所組成的 list
5、mapNotNull
9.4、元素操作符
1、contains
2、elementAt:返回給定index對(duì)應(yīng)的元素,如果index數(shù)組越界則會(huì)拋出 IndexOutOfBoundsException
3、elementAtOrElse: 越界則給出默認(rèn)值
4、elementAtOrNull
5、first
6、firstOrNull
7、indexOf
8、indexofFirst
9、indexOfLast
10、last
11、lastIndexOf
12、lastOrNull
13、single:返回符合給定函數(shù)的單個(gè)元素,如果沒有符合或超過一個(gè),則拋出異常
14、singleOrNull
9.5、生產(chǎn)操作符
1、merge:把兩個(gè)集合合并為一個(gè)新的,相同index的元素通過給定的函數(shù)進(jìn)行合并生成新的元素作為新集合中的一個(gè)元素,返回新集合。新集合的大小由最小的那個(gè)集合大小決定
2、partition: 把一個(gè)給定集合的分割為兩個(gè),第一個(gè)集合是由原集合每一項(xiàng)元素匹配給定函數(shù)條件返回 true 的元素組成,第二集合為false
3、plus
4、zip:返回由 pair 組成的 list,每個(gè) pair 由 兩個(gè)集合中相同index 的元素組成。該返回的 list 大小由最小的那個(gè)集合決定。
5、unzip:從包含 pair 的 List 中生成包含List的Pair
9.6、順序操作符
1、reverse:返回一個(gè)與指定list相反順序的list
2、sort:返回一個(gè)自然排序后的list
3、sortBy:指定函數(shù)排序
4、sortDescending:降序
5、sortDescendingBy:指定函數(shù)降序