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)用