Android開(kāi)發(fā)中Kotlin之于java那些不一樣的功能

感覺(jué)Kotlin對(duì)java不僅是一點(diǎn)點(diǎn)的改變,而是一種完全不同的體檢。習(xí)慣kotlin的簡(jiǎn)潔后,就知道java到底e有多啰嗦了。今天簡(jiǎn)單對(duì)比一下在Android開(kāi)發(fā)中kotlin在語(yǔ)言本身上就有哪些好用的功能。

可觀察屬性Delegates.observable。在Oc中早已有的功能,非常方便在狀態(tài)值切換時(shí)使用,再也不怕?tīng)顟B(tài)值改變時(shí)沒(méi)有調(diào)用到關(guān)聯(lián)的函數(shù)。

語(yǔ)法:

var max: Int by Delegates.observable(0) { property, oldValue, newValue -> 
  doSomething() 
}

在初始化時(shí)接受兩個(gè)參數(shù):初始值和修改時(shí)的處理程序。每當(dāng)給屬性賦值后會(huì)自動(dòng)調(diào)用該處理程。在處理狀態(tài)值時(shí)十分有用,不用再像Java里那樣狀態(tài)值每重新賦值就需要調(diào)用一次更新函數(shù)?,F(xiàn)在只需要在初始化時(shí)添加好更新函數(shù)即可。

如在tab頁(yè)切換時(shí):

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()
  ... 
        }
    }

在后面代碼中,不用再關(guān)心tabPosition修改后的更新,只管重新賦值就好了。

適用場(chǎng)景:狀態(tài)切換,目標(biāo)值監(jiān)聽(tīng),屬性值改變時(shí)的聯(lián)動(dòng)操作】

擴(kuò)展。能夠擴(kuò)展一個(gè)類(lèi)的新功能而無(wú)需要繼承該類(lèi),也不用使用如裝飾器那樣的設(shè)計(jì)模式。直接寫(xiě)直接引用。這是Oc中早已有的功能,而Java卻一直用不上。以致于在java代碼中,會(huì)出現(xiàn)一堆如StringUtils、ToastUtils..等靜態(tài)工具類(lèi)。使用時(shí)再將原對(duì)象當(dāng)作參數(shù)傳入,做一堆操作后再返回,產(chǎn)生大量冗余代碼。有了擴(kuò)展功能,能非常方便的對(duì)對(duì)象添加其他方法和使用代碼封裝。

語(yǔ)法:

函數(shù)擴(kuò)展:直接以fun 原類(lèi)名.擴(kuò)展函數(shù)名創(chuàng)建一個(gè)函數(shù)即可,在函數(shù)值中使用this來(lái)訪問(wèn)原對(duì)象; 常用于對(duì)原對(duì)象功能的添加。如對(duì)常用的StringUtils的封裝,如判斷當(dāng)前string是否為一個(gè)url:

fun String.isHttpUrl(): Boolean { 
  return !this.isEmpty() && (this.startsWith("http://") || this.startsWith("https://")) 
}

在使用時(shí)直接調(diào)用str.isHttpUrl()即可【AndroidStudio會(huì)自動(dòng)import】

屬性擴(kuò)展:直接以val 屬性名:get()=...來(lái)創(chuàng)建即可。如使用Retrofit封裝一個(gè)Api對(duì)象:

val Api
  get() = 
        RetrofitManager.instance
                .retrofit(builder = DefaultRetrofitBuilder.builder)
                .create(Api::class.java)!!

非空判斷,這個(gè)對(duì)于步步校驗(yàn),一路非空判斷的強(qiáng)迫癥來(lái)說(shuō)非常有用。用來(lái)擺脫Java中的xx==null,xx!=null的繁瑣的判斷。

  • ?.:相當(dāng)于調(diào)用的if not null,如:id = item?.id,相當(dāng)于if(item!=null) id=item.id else id=null,若想給個(gè)默認(rèn)值,則可以使用:id = item?.id ?: 0
  • !!:非空斷言運(yùn)算符(!!)將任何值轉(zhuǎn)換為非空類(lèi)型,若該值為空則拋出異常。如在必須非空時(shí)可使用Api.login(LoginReq(usename,pwd)).load(activity!!)
  • isNullOrEmpty()/isNullOrBlank()/isNotEmpty()/isNotBlank()/isEmpty()/isBlank():大量的內(nèi)部函數(shù)更方便操作對(duì)象值。

數(shù)據(jù)類(lèi):常用于數(shù)據(jù)解析實(shí)體。在Java中要對(duì)網(wǎng)絡(luò)響應(yīng)的數(shù)據(jù)解析通常都是先定義好的響應(yīng)的實(shí)體類(lèi),再進(jìn)行解析。而數(shù)據(jù)類(lèi)能更方便的定義實(shí)體類(lèi),并自動(dòng)添加好set/get方法。再也不需要大篇大篇的set/get。

  • 語(yǔ)法:data class A(val a:Int);對(duì)復(fù)雜的響應(yīng)實(shí)體可嵌套定義,如:
 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,延遲加載,這個(gè)在Java中也可通過(guò)延遲賦值來(lái)實(shí)現(xiàn),但都沒(méi)有kotlin來(lái)的直接好用。

如單例模式的寫(xiě)法:

 companion object { 
  val instance: UserManager by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { UserManager() } 
}

  • 在Andoird開(kāi)發(fā)中更好用的功能在于對(duì)釋放OnCreate()方法中的一大堆的初始化操作,如定義并初始化一個(gè)Adapter,可以直接在定義時(shí)賦值:
 private val adapter: AreaAdapter by lazy { AreaAdapter(this) } 

【需注意,這種使用了上下文context的延遲加載,只能在生命周期方法中調(diào)用,不能在后面的定義的值時(shí)引用?!?/p>

告別方法重載:在Java開(kāi)發(fā)中,對(duì)于同名不能同參的函數(shù)即方法重載。若參數(shù)過(guò)多,在封裝函數(shù)時(shí)會(huì)寫(xiě)好幾個(gè)同名函數(shù),造成大量代碼冗余。而在Kotlin中,有了默認(rèn)參數(shù)的支持,只需要定義一個(gè)函數(shù)即可。如:

fun share(activity: Activity, url: String, title: String = "", desc: String = "", shareResult: (Boolean) -> Unit = {}) {} 

lambda使用,無(wú)疑是對(duì)Java最期待而又用不上的功能,在Kotlin終于可以使用block當(dāng)參數(shù),省去了大量匿名內(nèi)部類(lèi)的創(chuàng)建。在Java中的回調(diào)只能先定義一個(gè)接口,外部定義一個(gè)匿名內(nèi)部類(lèi)做一個(gè)參數(shù)傳入函數(shù)體,函數(shù)內(nèi)部在做了一連串操作后再使用該參數(shù)執(zhí)行。在Kotlin中只需定義一個(gè)block即可,如定義一個(gè)點(diǎn)擊事件,并給個(gè)默認(rèn)值:

  fun updateItem(item: Item, click: () -> Unit) {//沒(méi)有默認(rèn)實(shí)現(xiàn),必須傳入} 
  fun updateItem(item: Item, click: () -> Unit={}}) {//有空默認(rèn)值,非必傳} 
  fun updateItem(item: Item, click: (Int) -> Boolean={}}) {//帶參數(shù)的block} 

  //保存?zhèn)魅氲腷lock 
  var click: () -> Unit={} 
  fun updateItem(item: Item, click: () -> Unit}) { 
        this.click = click
  //內(nèi)部調(diào)用時(shí)使用 
        click.invoke()
    }
  //外部調(diào)用時(shí)直接使用 
    xx.updateItem(item){
  //click action 
    }

list的foreach:對(duì)列表的遍歷,可完善代替for in的遍歷。如:

list.forEach { println("it = $it") }

另外kotlin提供了大師的擴(kuò)展函數(shù)用于對(duì)列表操作。如大家都知道在Java中不能在對(duì)一個(gè)List遍歷時(shí)動(dòng)態(tài)刪除,否則會(huì)拋出ConcurrentModificationException異常,一般會(huì)建一個(gè)臨時(shí)的list,保存需要?jiǎng)h除的對(duì)象,在遍歷結(jié)束后再調(diào)用removeAll來(lái)實(shí)現(xiàn)。而在Kotlin中可直接在在遍歷中刪除:

list.filter { it -> it == "a" }.forEach { list.remove(it) }

【另外Kotlin還添加對(duì)列表的排序(sort),轉(zhuǎn)map(associate),去重(distinct),過(guò)濾(filter),反轉(zhuǎn)(asReversed),查找(find),最值(max/min),遞減(reduce),求和(sumBy/fold)等函數(shù),非常方便集合的操作】

mapTo 動(dòng)態(tài)初始化數(shù)組:用于遍歷一個(gè)集合,并填充到另一個(gè)集合中。類(lèi)似于Java下for循環(huán)遍歷集合,再將新的對(duì)象add到新的集合中。如遍歷響應(yīng)數(shù)據(jù)填充坐標(biāo)集合:

  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:引用一個(gè)實(shí)例,并調(diào)用多個(gè)方法。還可以在調(diào)用過(guò)程中做其他操作。如調(diào)用Retrofit實(shí)例:

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:常用的幾種擴(kuò)展函數(shù),非常方便對(duì)對(duì)象的判斷及操作,附上使用表格:

函數(shù)名 定義 block參數(shù) 閉包返回返回值 函數(shù)返回值 extension 其他
repeat fun repeat(times: Int, action: (Int) -> Unit) 無(wú) Unit Unit 普通函數(shù)
with fun with(receiver: T, f: T.() -> R): R = receiver.f() 無(wú),可以使用this Any 閉包返回 普通函數(shù)
run run(block: () -> R): R 無(wú) Any 閉包返回 普通函數(shù)
let fun T.let(f: (T) -> R): R it Any 閉包返回
apply fun T.apply(f: T.() -> Unit): T 無(wú),可以使用this Unit this
run fun T.run(f: T.() -> R): R 無(wú),可以使用this Any 閉包返回
also fun T.also(block: (T) -> Unit): T it Unit 閉包返回
takeIf fun T.takeIf(predicate: (T) -> Boolean): T? it Boolean this 或 null 閉包返回類(lèi)型必須是Boolean
takeUnless fun T.takeUnless(predicate: (T) -> Boolean): T? it Boolean this 或 null 閉包返回類(lèi)型必須是Boolean

kotlinx對(duì)xml的注解解析。在Android最繁瑣莫過(guò)于對(duì)findViewById,也有很多的第三方框架如butterknife來(lái)的簡(jiǎn)化這部分代碼。但在使用了kotlinx后才發(fā)現(xiàn)定義組件的對(duì)象都是多余的。只須在根項(xiàng)目下引用gradle插件:classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version",在xml定義了一個(gè)組件后,在代碼時(shí)直接使用id來(lái)就可以了,根本不需要定義變量,再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{}代碼塊中調(diào)用infalte函數(shù)引入xml資源,在如:

class ResultView(context: Context) : ConstraintLayout(context) { 
    init {
        inflate(context, R.layout.view_result, this) 
    }

    fun addListener(clickAction: () -> Unit) { 
        btn_result.setOnClickListener { clickAction.invoke() } 
    }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容