可觀察屬性Delegates.observable。在Oc中早已有的功能,很是方便在狀態值切換時使用,不再怕狀態值改變時沒有調用到關聯的函數。html
語法:java
var max: Int by Delegates.observable(0) { property, oldValue, newValue ->
doSomething()
}
複製代碼
在初始化時接受兩個參數:初始值和修改時的處理程序。每當給屬性賦值後會自動調用該處理程。在處理狀態值時十分有用,不用再像Java裏那樣狀態值每從新賦值就須要調用一次更新函數。如今只須要在初始化時添加好更新函數便可。android
如在tab頁切換時:設計模式
private var tabPosition: Int by Delegates.observable(0) { _: KProperty<*>, _: Int, tab: Int ->
if (tab == 0) {
tv_title_name.text = getString(R.string.title0)
loadData0()
...
} else {
tv_title_name.text = getString(R.string.title1)
loadData1()
...
}
}
複製代碼
在後面代碼中,不用再關心tabPosition修改後的更新,只管從新賦值就行了。api
【適用場景:狀態切換,目標值監聽,屬性值改變時的聯動操做】數組
擴展。可以擴展一個類的新功能而無須要繼承該類,也不用使用如裝飾器那樣的設計模式。直接寫直接引用。這是Oc中早已有的功能,而Java卻一直用不上。以至於在java代碼中,會出現一堆如StringUtils、ToastUtils..等靜態工具類。使用時再將原對象看成參數傳入,作一堆操做後再返回,產生大量冗餘代碼。有了擴展功能,能很是方便的對對象添加其餘方法和使用代碼封裝。bash
語法:網絡
函數擴展:直接以fun 原類名.擴展函數名建立一個函數便可,在函數值中使用this來訪問原對象; 經常使用於對原對象功能的添加。如對經常使用的StringUtils的封裝,如判斷當前string是否爲一個url:閉包
fun String.isHttpUrl(): Boolean {
return !this.isEmpty() && (this.startsWith("http://") || this.startsWith("https://"))
}
複製代碼
在使用時直接調用str.isHttpUrl()便可【AndroidStudio會自動import】app
屬性擴展:直接以val 屬性名:get()=...來建立便可。如使用Retrofit封裝一個Api對象:
val Api
get() =
RetrofitManager.instance
.retrofit(builder = DefaultRetrofitBuilder.builder)
.create(Api::class.java)!!
複製代碼
非空判斷,這個對於步步校驗,一路非空判斷的強迫症來講很是有用。用來擺脫Java中的xx==null,xx!=null的繁瑣的判斷。
數據類:經常使用於數據解析實體。在Java中要對網絡響應的數據解析一般都是先定義好的響應的實體類,再進行解析。而數據類能更方便的定義實體類,並自動添加好set/get方法。不再須要大篇大篇的set/get。
data class NewsDetailResp(val data: NewsDetail) : BaseResponse() {
data class NewsDetail(val total: Double,
val detail: ArrayList<Detail>) {
data class Detail(
val id: Long,
val userid: Long
)
}
}
複製代碼
lazy,延遲加載,這個在Java中也可經過延遲賦值來實現,但都沒有kotlin來的直接好用。
如單例模式的寫法:
companion object {
val instance: UserManager by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { UserManager() }
}
複製代碼
private val adapter: AreaAdapter by lazy { AreaAdapter(this) }
複製代碼
【需注意,這種使用了上下文context的延遲加載,只能在生命週期方法中調用,不能在後面的定義的值時引用。】
告別方法重載:在Java開發中,對於同名不能同參的函數即方法重載。若參數過多,在封裝函數時會寫好幾個同名函數,形成大量代碼冗餘。而在Kotlin中,有了默認參數的支持,只須要定義一個函數便可。如:
fun share(activity: Activity, url: String, title: String = "", desc: String = "", shareResult: (Boolean) -> Unit = {}) {}
複製代碼
lambda使用,無疑是對Java最期待而又用不上的功能,在Kotlin終於可使用block當參數,省去了大量匿名內部類的建立。在Java中的回調只能先定義一個接口,外部定義一個匿名內部類作一個參數傳入函數體,函數內部在作了一連串操做後再使用該參數執行。在Kotlin中只需定義一個block便可,如定義一個點擊事件,並給個默認值:
fun updateItem(item: Item, click: () -> Unit) {//沒有默認實現,必須傳入}
fun updateItem(item: Item, click: () -> Unit={}}) {//有空默認值,非必傳}
fun updateItem(item: Item, click: (Int) -> Boolean={}}) {//帶參數的block}
//保存傳入的block
var click: () -> Unit={}
fun updateItem(item: Item, click: () -> Unit}) {
this.click = click
//內部調用時使用
click.invoke()
}
//外部調用時直接使用
xx.updateItem(item){
//click action
}
複製代碼
list的foreach:對列表的遍歷,可完善代替for in的遍歷。如:
list.forEach { println("it = $it") }
複製代碼
另外kotlin提供了大師的擴展函數用於對列表操做。如你們都知道在Java中不能在對一個List遍歷時動態刪除,不然會拋出ConcurrentModificationException異常,通常會建一個臨時的list,保存須要刪除的對象,在遍歷結束後再調用removeAll來實現。而在Kotlin中可直接在在遍歷中刪除:
list.filter { it -> it == "a" }.forEach { list.remove(it) }
複製代碼
【另外Kotlin還添加對列表的排序(sort),轉map(associate),去重(distinct),過濾(filter),反轉(asReversed),查找(find),最值(max/min),遞減(reduce),求和(sumBy/fold)等函數,很是方便集合的操做】
mapTo 動態初始化數組:用於遍歷一個集合,並填充到另外一個集合中。相似於Java下for循環遍歷集合,再將新的對象add到新的集合中。如遍歷響應數據填充座標集合:
val entries = ArrayList<Entry>()
(start until datas.size).mapTo(entries) {
val x = datas[it].time.toFloat()
val y = datas[it].value.toFloat()
Entry(x, y)
}
複製代碼
with:引用一個實例,並調用多個方法。還能夠在調用過程當中作其餘操做。如調用Retrofit實例:
val retrofit = with(Retrofit.Builder()) {
baseUrl(baseUrl)
addConverterFactory(builder.factory)
client(okHttpClient(builder))
//do other something
//rxJavaAdapter
addCallAdapterFactory(RxJava2CallAdapterFactory.create())
build()
}
複製代碼
apply/takeIf/also/takeUnless/let:經常使用的幾種擴展函數,很是方便對對象的判斷及操做,附上使用表格:
函數名 | 定義 | block參數 | 閉包返回返回值 | 函數返回值 | extension | 其餘 |
---|---|---|---|---|---|---|
repeat | fun repeat(times: Int, action: (Int) -> Unit) | 無 | Unit | Unit | 否 | 普通函數 |
with | fun with(receiver: T, f: T.() -> R): R = receiver.f() | 無,可使用this | Any | 閉包返回 | 否 | 普通函數 |
run | run(block: () -> R): R | 無 | Any | 閉包返回 | 否 | 普通函數 |
let | fun T.let(f: (T) -> R): R | it | Any | 閉包返回 | 是 | |
apply | fun T.apply(f: T.() -> Unit): T | 無,可使用this | Unit | this | 是 | |
run | fun T.run(f: T.() -> R): R | 無,可使用this | Any | 閉包返回 | 是 | |
also | fun T.also(block: (T) -> Unit): T | it | Unit | 閉包返回 | 是 | |
takeIf | fun T.takeIf(predicate: (T) -> Boolean): T? | it | Boolean | this 或 null | 是 | 閉包返回類型必須是Boolean |
takeUnless | fun T.takeUnless(predicate: (T) -> Boolean): T? | it | Boolean | this 或 null | 是 | 閉包返回類型必須是Boolean |
kotlinx對xml的註解解析。在Android最繁瑣莫過於對findViewById,也有不少的第三方框架如butterknife來的簡化這部分代碼。但在使用了kotlinx後才發現定義組件的對象都是多餘的。只須在根項目下引用gradle插件:**classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version",**在xml定義了一個組件後,在代碼時直接使用id來就能夠了,根本不須要定義變量,再findViewById去賦值。如在xml中定義:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="yoyo"
android:textColor="@color/text_black"
android:textSize="@dimen/text_16"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
複製代碼
在Activity中使用setContentView引用了該xml後,則能夠直接使用tv_name.text="jim"這樣的代碼一賦值。若在自定義的view,須要init{}代碼塊中調用infalte函數引入xml資源,在如:
class ResultView(context: Context) : ConstraintLayout(context) {
init {
inflate(context, R.layout.view_result, this)
}
fun addListener(clickAction: () -> Unit) {
btn_result.setOnClickListener { clickAction.invoke() }
}
}
複製代碼