Kotlin實(shí)戰(zhàn)之run, with, let, also , apply函數(shù)使用技巧

with和其它通用擴(kuò)展函數(shù)

with的用法和其它通用的擴(kuò)展函數(shù)的用法有區(qū)別,對(duì)于下面這段代碼做的是同樣一件事。它們的不同之處就是一個(gè)使用了with(T)函數(shù),而另一個(gè)則是使用了T.run函數(shù)。

with(webView.settings){
    javaScriptEnabled = true
    databaseEnabled = true
}

webView.settings.run { 
    javaScriptEnabled = true
    databaseEnabled = true
}

單純從以上代碼是看不出哪個(gè)更好?現(xiàn)在假設(shè)一種場(chǎng)景,想象一下如果 webview.settings 可能為空,那么下面兩種方式實(shí)現(xiàn)如何去修改呢?再次看一下下面這段代碼

with(webView.settings){
    this?.javaScriptEnabled = true
    this?.databaseEnabled = true
}

webView.settings?.run { 
    javaScriptEnabled = true
    databaseEnabled = true
}

當(dāng)然是T.run方法會(huì)更好,因?yàn)槲覀兛梢栽谑褂眠@些函數(shù)之前可以進(jìn)行對(duì)null的檢查。

對(duì)于with也是存在一個(gè)返回值,它也是會(huì)返回在這個(gè)作用域當(dāng)中的最后一個(gè)對(duì)象。

run,let

作用域中接收者this和it
kotlin中這個(gè)五個(gè)擴(kuò)展函數(shù),在它們的作用域中的接收者可以是this或者是it。對(duì)比一下T.run和T.let函數(shù),兩個(gè)函數(shù)也是十分的相似。

stringVariable?.run {
    println("字符串的長(zhǎng)度為$length")
}

stringVariable?.let {
    println("字符串的長(zhǎng)度為 ${it.length}")
}

在T.run函數(shù)中通過this來獲取stringVariable對(duì)象,而在T.let函數(shù)中通過it來取出stringVariable對(duì)象。當(dāng)然可以為it重新命名。如果我們不想覆蓋外部作用域的this,這時(shí)候去使用T.let會(huì)更加的方便

let擴(kuò)展函數(shù)的實(shí)際上是一個(gè)作用域函數(shù),當(dāng)你需要去定義一個(gè)變量在一個(gè)特定的作用域范圍內(nèi),let函數(shù)的是一個(gè)不錯(cuò)的選擇;let函數(shù)另一個(gè)作用就是可以避免寫一些判斷null的操作

also,apply

在這些作用域中它們都會(huì)存在一個(gè)返回值。在上面的講述的run,with,T.run,T.let中它們返回的都是作用域中最后一個(gè)對(duì)象。當(dāng)然它們所返回的值是允許和接受者it或者this對(duì)象的類型不同。

val original = "abc"
original.let {
    println("The original String is $it") // "abc"
    it.reversed() 
}.let {
    println("The reverse String is $it") // "cba"
    it.length  
}.let {
    println("The length of the String is $it") // 3
}

但并不是所有的擴(kuò)展函數(shù)都是返回作用域的最后一個(gè)對(duì)象。例如T.also函數(shù)。

original.also {
    println("The original String is $it") // "abc"
    it.reversed() 
}.also {
    println("The reverse String is ${it}") // "abc"
    it.length  
}.also {
    println("The length of the String is ${it}") // "abc"
}

從上面兩段代碼可以看出T.let和T.also的返回值使不同的。T.let返回的是作用域中的最后一個(gè)對(duì)象,它的值和類型都可以改變。但是T.also不管調(diào)用多少次返回的都是原來的original對(duì)象。

對(duì)于T.let和T.also都能夠進(jìn)行鏈?zhǔn)讲僮鳎敲次覀儸F(xiàn)在結(jié)合一下T.let和T.also的鏈?zhǔn)秸{(diào)用來看一下在實(shí)際場(chǎng)景中的應(yīng)用。

//原始函數(shù)
fun makeDir(path: String): File  {
    val result = File(path)
    result.mkdirs()
    return result
}
//通過let和also的鏈?zhǔn)秸{(diào)用改進(jìn)后的函數(shù)
fun makeDir(path: String) = path.let{ File(it) }.also{ it.mkdirs() }

擴(kuò)展函數(shù)的特性

到目前為止除了T.apply沒有使用到以外,根據(jù)上面的用法我們可以總結(jié)出來這些擴(kuò)展函數(shù)的三大特性。

1. 它們都有自己的作用域

2. 它們作用域中的接收者是this或者it

3. 它們都有一個(gè)返回值,返回最后一個(gè)對(duì)象(this)或者調(diào)用者自身(itself)

由此可想到對(duì)于T.apply無非也就是這三個(gè)特性。對(duì)于T.apply它作用域中的接收者是this,并且返回的調(diào)用者T。因此,T.apply的其中一個(gè)使用場(chǎng)景可以用來創(chuàng)建一個(gè)Fragment,代碼如下所示:

// 使用普通的方法創(chuàng)建一個(gè)Fragment
fun newInstance(args: Bundle) : MyFragment {
    val fragment = MyFragment()
    fragment.arguments = args
    return fragment
}

// 通過apply來改善原有的方法創(chuàng)建一個(gè)Fragment
fun newInstance(args: Bundle) 
              = MyFragment().apply { arguments = args }

我們也能夠通過T.apply的鏈?zhǔn)秸{(diào)用創(chuàng)建一個(gè)Intent:

// 普通創(chuàng)建Intent方法
fun createIntent(intentData: String, intentAction: String): Intent {
    val intent = Intent()
    intent.action = intentAction
    intent.data=Uri.parse(intentData)
    return intent
}

// 通過apply函數(shù)的鏈?zhǔn)秸{(diào)用創(chuàng)建Intent
fun createIntent(intentData: String, intentAction: String) =
        Intent().apply { action = intentAction }
                .apply { data = Uri.parse(intentData) }

函數(shù)的選用

let
let擴(kuò)展函數(shù)的實(shí)際上是一個(gè)作用域函數(shù),當(dāng)你需要去定義一個(gè)變量在一個(gè)特定的作用域范圍內(nèi),let函數(shù)的是一個(gè)不錯(cuò)的選擇;let函數(shù)另一個(gè)作用就是可以避免寫一些判斷null的操作。

let函數(shù)的一般結(jié)構(gòu)

object.let{
it.todo()//在函數(shù)體內(nèi)使用it替代object對(duì)象去訪問其公有的屬性和方法
...
}

//另一種用途 判斷object為null的操作
object?.let{//表示object不為null的條件下,才會(huì)去執(zhí)行l(wèi)et函數(shù)體
it.todo()
}

let函數(shù)使用前后的對(duì)比

mVideoPlayer?.setVideoView(activity.course_video_view)
    mVideoPlayer?.setControllerView(activity.course_video_controller_view)
    mVideoPlayer?.setCurtainView(activity.course_video_curtain_view)
------------------------------------------------------------------------------------------------------------------------------
mVideoPlayer?.let {
       it.setVideoView(activity.course_video_view)
       it.setControllerView(activity.course_video_controller_view)
       it.setCurtainView(activity.course_video_curtain_view)
}

let函數(shù)適用的場(chǎng)景

場(chǎng)景一: 最常用的場(chǎng)景就是使用let函數(shù)處理需要針對(duì)一個(gè)可null的對(duì)象統(tǒng)一做判空處理。
場(chǎng)景二: 然后就是需要去明確一個(gè)變量所處特定的作用域范圍內(nèi)可以使用
with

with函數(shù)使用的一般結(jié)構(gòu)

with(object){
   //todo
 }

with函數(shù)使用前后的對(duì)比

override fun onBindViewHolder(holder: ViewHolder, position: Int){
   val item = getItem(position)?: return
   with(item){
      holder.tvNewsTitle.text = StringUtils.trimToEmpty(titleEn)
       holder.tvNewsSummary.text = StringUtils.trimToEmpty(summary)
       holder.tvExtraInf.text = "難度:$gradeInfo | 單詞數(shù):$length | 讀后感: $numReviews"
   }
}
------------------------------------------------------------------------------------------------------------------------------
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
   ArticleSnippet item = getItem(position);
        if (item == null) {
            return;
        }
        holder.tvNewsTitle.setText(StringUtils.trimToEmpty(item.titleEn));
        holder.tvNewsSummary.setText(StringUtils.trimToEmpty(item.summary));
        String gradeInfo = "難度:" + item.gradeInfo;
        String wordCount = "單詞數(shù):" + item.length;
        String reviewNum = "讀后感:" + item.numReviews;
        String extraInfo = gradeInfo + " | " + wordCount + " | " + reviewNum;
        holder.tvExtraInfo.setText(extraInfo);
}

with函數(shù)的適用的場(chǎng)景
適用于調(diào)用同一個(gè)類的多個(gè)方法時(shí),可以省去類名重復(fù),直接調(diào)用類的方法即可,經(jīng)常用于Android中RecyclerView中onBinderViewHolder中,數(shù)據(jù)model的屬性映射到UI上

run

run函數(shù)使用的一般結(jié)構(gòu)

object.run{
//todo
}

run函數(shù)使用前后對(duì)比

override fun onBindViewHolder(holder: ViewHolder, position: Int){
   val item = getItem(position)?: return
   with(item){
      holder.tvNewsTitle.text = StringUtils.trimToEmpty(titleEn)
       holder.tvNewsSummary.text = StringUtils.trimToEmpty(summary)
       holder.tvExtraInf = "難度:$gradeInfo | 單詞數(shù):$length | 讀后感: $numReviews"
       ...   
   }
}
// 使用后
override fun onBindViewHolder(holder: ViewHolder, position: Int){
  getItem(position)?.run{
      holder.tvNewsTitle.text = StringUtils.trimToEmpty(titleEn)
       holder.tvNewsSummary.text = StringUtils.trimToEmpty(summary)
       holder.tvExtraInf = "難度:$gradeInfo | 單詞數(shù):$length | 讀后感: $numReviews"
       ...   
   }
}

run函數(shù)使用場(chǎng)景

適用于let,with函數(shù)任何場(chǎng)景。因?yàn)閞un函數(shù)是let,with兩個(gè)函數(shù)結(jié)合體,準(zhǔn)確來說它彌補(bǔ)了let函數(shù)在函數(shù)體內(nèi)必須使用it參數(shù)替代對(duì)象,在run函數(shù)中可以像with函數(shù)一樣可以省略,直接訪問實(shí)例的公有屬性和方法,另一方面它彌補(bǔ)了with函數(shù)傳入對(duì)象判空問題,在run函數(shù)中可以像let函數(shù)一樣做判空處理

apply

apply函數(shù)使用的一般結(jié)構(gòu)

object.apply{
//todo
}

apply函數(shù)使用前后的對(duì)比

//使用前
mSheetDialogView = View.inflate(activity, R.layout.biz_exam_plan_layout_sheet_inner, null)
        mSheetDialogView.course_comment_tv_label.paint.isFakeBoldText = true
        mSheetDialogView.course_comment_tv_score.paint.isFakeBoldText = true
        mSheetDialogView.course_comment_tv_cancel.paint.isFakeBoldText = true
        mSheetDialogView.course_comment_tv_confirm.paint.isFakeBoldText = true
        mSheetDialogView.course_comment_seek_bar.max = 10
        mSheetDialogView.course_comment_seek_bar.progress = 0
//使用后
mSheetDialogView = View.inflate(activity, R.layout.biz_exam_plan_layout_sheet_inner, null).apply{
   course_comment_tv_label.paint.isFakeBoldText = true
   course_comment_tv_score.paint.isFakeBoldText = true
   course_comment_tv_cancel.paint.isFakeBoldText = true
   course_comment_tv_confirm.paint.isFakeBoldText = true
   course_comment_seek_bar.max = 10
   course_comment_seek_bar.progress = 0

}
//多級(jí)判空
    if (mSectionMetaData == null || mSectionMetaData.questionnaire == null || mSectionMetaData.section == null) {
            return;
        }
        if (mSectionMetaData.questionnaire.userProject != null) {
            renderAnalysis();
            return;
        }
        if (mSectionMetaData.section != null && !mSectionMetaData.section.sectionArticles.isEmpty()) {
            fetchQuestionData();
            return;
        }

    mSectionMetaData?.apply{
    //mSectionMetaData不為空的時(shí)候操作mSectionMetaData
    }?.questionnaire?.apply{
    //questionnaire不為空的時(shí)候操作questionnaire
    }?.section?.apply{
    //section不為空的時(shí)候操作section
    }?.sectionArticle?.apply{
    //sectionArticle不為空的時(shí)候操作sectionArticle
    }

also

also函數(shù)使用的一般結(jié)構(gòu)

object.also{
//todo
}

also函數(shù)的適用場(chǎng)景

適用于let函數(shù)的任何場(chǎng)景,also函數(shù)和let很像,只是唯一的不同點(diǎn)就是let函數(shù)最后的返回值是最后一行的返回值而also函數(shù)的返回值是返回當(dāng)前的這個(gè)對(duì)象。一般可用于多個(gè)擴(kuò)展函數(shù)鏈?zhǔn)秸{(diào)用

image
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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