柚子快報(bào)邀請碼778899分享:
柚子快報(bào)邀請碼778899分享:
2-3-1 第1種寫法
這是原始代碼,它的本質(zhì)是用 object 關(guān)鍵字定義了一個(gè)匿名內(nèi)部類:
image.setOnClickListener(object: View.OnClickListener {
override fun onClick(v: View?) {
gotoPreview(v)
}
})
2-3-2 第2種寫法
如果我們刪掉 object 關(guān)鍵字,它就是 Lambda 表達(dá)式了,因此它里面 override 的方法也要跟著刪掉:
image.setOnClickListener(View.OnClickListener { view: View? ->
gotoPreview(view)
})
上面的 View.OnClickListener 被稱為: SAM Constructor—— SAM 構(gòu)造器,它是編譯器為我們生成的。Kotlin 允許我們通過這種方式來定義 Lambda 表達(dá)式。
思考題:
這時(shí)候,View.OnClickListener {} 在語義上是 Lambda 表達(dá)式,但在語法層面還是匿名內(nèi)部類。這句話對不對?
2-3-3 第3種寫法
由于 Kotlin 的 Lambda 表達(dá)式是不需要 SAM Constructor的,所以它也可以被刪掉。
image.setOnClickListener({ view: View? ->
gotoPreview(view)
})
2-3-4 第4種寫法
由于 Kotlin 支持類型推導(dǎo),所以 View? 可以被刪掉:
image.setOnClickListener({ view ->
gotoPreview(view)
})
2-3-5 第5種寫法
當(dāng) Kotlin Lambda 表達(dá)式只有一個(gè)參數(shù)的時(shí)候,它可以被寫成 it。
image.setOnClickListener({ it ->
gotoPreview(it)
})
2-3-6 第6種寫法
Kotlin Lambda 的 it 是可以被省略的:
image.setOnClickListener({
gotoPreview(it)
})
2-3-7 第7種寫法
當(dāng) Kotlin Lambda 作為函數(shù)的最后一個(gè)參數(shù)時(shí),Lambda 可以被挪到外面:
image.setOnClickListener() {
gotoPreview(it)
}
2-3-8 第8種寫法
當(dāng) Kotlin 只有一個(gè) Lambda 作為函數(shù)參數(shù)時(shí),() 可以被省略:
image.setOnClickListener {
gotoPreview(it)
}
按照這個(gè)流程,在 IDE 里多寫幾遍,你自然就會(huì)理解了。一定要寫,看文章是記不住的。
2-4 函數(shù)類型,高階函數(shù),Lambda表達(dá)式三者之間的關(guān)系
將函數(shù)的參數(shù)類型和返回值類型抽象出來后,就得到了函數(shù)類型。(View) -> Unit 就代表了參數(shù)類型是 View 返回值類型為 Unit 的函數(shù)類型。 如果一個(gè)函數(shù)的參數(shù)或者返回值的類型是函數(shù)類型,那這個(gè)函數(shù)就是高階函數(shù)。很明顯,我們剛剛就寫了一個(gè)高階函數(shù),只是它比較簡單而已。 Lambda 就是函數(shù)的一種簡寫
一張圖看懂:函數(shù)類型,高階函數(shù),Lambda表達(dá)式三者之間的關(guān)系:
回過頭再看官方文檔提供的例子:
fun
initial: R,
combine: (acc: R, nextElement: T) -> R
): R {
var accumulator: R = initial
for (element: T in this) {
accumulator = combine(accumulator, element)
}
return accumulator
}
看看這個(gè)函數(shù)類型:(acc: R, nextElement: T) -> R,是不是瞬間就懂了呢?這個(gè)函數(shù)接收兩個(gè)參數(shù),第一個(gè)參數(shù)類型是R,第二個(gè)參數(shù)是T,函數(shù)的返回類型是R。
3. 帶接收者(Receiver)的函數(shù)類型:A.(B,C) -> D
說實(shí)話,這個(gè)名字也對初學(xué)者不太友好:帶接收者的函數(shù)類型(Function Types With Receiver),這里面的每一個(gè)字(單詞)我都認(rèn)識,但單憑這么點(diǎn)信息,初學(xué)者真的很難理解它的本質(zhì)。
還是繞不開一個(gè)問題:為什么?
3-1 為什么要引入:帶接收者的函數(shù)類型?
我們在上一章節(jié)中提到過,用 apply 來簡化邏輯,我們是這樣寫的:
修改前:
if (user != null) {
…
username.text = user.name
website.text = user.blog
image.setOnClickListener { gotoImagePreviewActivity(user) }
}
修改后:
user?.apply {
…
username.text = name
website.text = blog
image.setOnClickListener { gotoImagePreviewActivity(this) }
}
請問:這個(gè) apply 方法應(yīng)該怎么實(shí)現(xiàn)?
上面的寫法其實(shí)是簡化后的 Lambda 表達(dá)式,讓我們來反推,看看它簡化前是什么樣的:
// apply 肯定是個(gè)函數(shù),所以有 (),只是被省略了
user?.apply() {
…
}
// Lambda 肯定是在 () 里面
user?.apply({ … })
// 由于 gotoImagePreviewActivity(this) 里的 this 代表了 user
// 所以 user 應(yīng)該是 apply 函數(shù)的一個(gè)參數(shù),而且參數(shù)名為:this
user?.apply({ this: User -> … })
所以,現(xiàn)在問題非常明確了,apply 其實(shí)接收一個(gè) Lambda 表達(dá)式:{ this: User -> ... }。讓我們嘗試來實(shí)現(xiàn)這個(gè) apply 方法:
fun User.apply(block: (self: User) -> Unit): User{
block(self)
return this
}
user?.apply { self: User ->
…
username.text = self.name
website.text = self.blog
image.setOnClickListener { gotoImagePreviewActivity(this) }
}
由于 Kotlin 里面的函數(shù)形參是不允許被命名為 this 的,因此我這里用的 self,我們自己寫出來的 apply 仍然還要通過 self.name 這樣的方式來訪問成員變量,但 Kotlin 的語言設(shè)計(jì)者能做到這樣:
// 改為 this
// ↓
fun User.apply(block: (this: User) -> Unit): User{
// 這里還要傳參數(shù)
// ↓
block(this)
return this
}
user?.apply { this: User ->
…
// this 可以省略
// ↓
username.text = this.name
website.text = blog
image.setOnClickListener { gotoImagePreviewActivity(this) }
}
從上面的例子能看到,我們反推的 apply 實(shí)現(xiàn)比較繁瑣,需要我們自己調(diào)用:block(this),因此 Kotlin 引入了帶接收者的函數(shù)類型,可以簡化 apply 的定義:
// 帶接收者的函數(shù)類型
// ↓
fun User.apply(block: User.() -> Unit): User{
// 不用再傳this
// ↓
block()
return this
}
user?.apply { this: User ->
…
username.text = this.name
website.text = this.blog
image.setOnClickListener { gotoImagePreviewActivity(this) }
}
現(xiàn)在,關(guān)鍵來了。上面的 apply 方法是不是看起來就像是在 User 里增加了一個(gè)成員方法 apply()?
class User() {
val name: String = “”
val blog: String = “”
fun apply() {
// 成員方法可以通過 this 訪問成員變量
username.text = this.name
website.text = this.blog
image.setOnClickListener { gotoImagePreviewActivity(this) }
}
}
所以,從外表上看,帶接收者的函數(shù)類型,就等價(jià)于成員方法。但從本質(zhì)上講,它仍是通過編譯器注入 this 來實(shí)現(xiàn)的。
一張圖總結(jié):
思考題2:
帶接收者的函數(shù)類型,是否也能代表擴(kuò)展函數(shù)?
思考題3:
請問:A.(B,C) -> D 代表了一個(gè)什么樣的函數(shù)?
4. HTML Kotlin DSL 實(shí)戰(zhàn)
官方文檔在高階函數(shù)的章節(jié)里提到了:用高階函數(shù)來實(shí)現(xiàn) 類型安全的 HTML 構(gòu)建器。官方文檔的例子比較復(fù)雜,讓我們來寫一個(gè)簡化版的練練手吧。
4-1 效果展示:
val htmlContent = html {
head {
title { “Kotlin Jetpack In Action” }
}
body {
h1 { “Kotlin Jetpack In Action”}
p { “-----------------------------------------” }
p { “A super-simple project demonstrating how to use Kotlin and Jetpack step by step.” }
p { “-----------------------------------------” }
p { “I made this project as simple as possible,” +
" so that we can focus on how to use Kotlin and Jetpack" +
" rather than understanding business logic." }
p {“We will rewrite it from “Java + MVC” to” +
" “Kotlin + Coroutines + Jetpack + Clean MVVM”," +
" line by line, commit by commit."}
p { “-----------------------------------------” }
p { “ScreenShot:” }
img(src = “https://user-gold-cdn.xitu.io/2020/6/15/172b55ce7bf25419?imageslim”,
alt = “Kotlin Jetpack In Action”)
}
}.toString()
println(htmlContent)
以上代碼輸出的內(nèi)容是這樣的:
Kotlin Jetpack In Action
Kotlin Jetpack In Action
A super-simple project demonstrating how to use Kotlin and Jetpack step by step.
I made this project as simple as possible, so that we can focus on how to use Kotlin and Jetpack rather than understanding business logic.
We will rewrite it from “Java + MVC” to “Kotlin + Coroutines + Jetpack + Clean MVVM”, line by line, commit by commit.
ScreenShot:
4-2 HTML Kotlin DSL 實(shí)現(xiàn)
4-2-1 定義節(jié)點(diǎn)元素的接口
interface Element {
// 每個(gè)節(jié)點(diǎn)都需要實(shí)現(xiàn) render 方法
fun render(builder: StringBuilder, indent: String): String
}
所有的 HTML 節(jié)點(diǎn)都要實(shí)現(xiàn) Element 接口,并且在 render 方法里實(shí)現(xiàn) HTML 代碼的拼接:
4-2-2 定義基礎(chǔ)類
/**
每個(gè)節(jié)點(diǎn)都有 name,content: Kotlin Jetpack In Action
*/
open class BaseElement(val name: String, val content: String = “”) : Element {
// 每個(gè)節(jié)點(diǎn),都會(huì)有很多子節(jié)點(diǎn)
val children = ArrayList()
// 存放節(jié)點(diǎn)參數(shù):,里面的 src,alt
val hashMap = HashMap
/**
拼接 Html: Kotlin Jetpack In Action
*/
override fun render(builder: StringBuilder, indent: String): String {
builder.append(“
i
n
d
e
n
t
<
indent<
indent
if (content.isNotBlank()) {
builder.append("
i
n
d
e
n
t
indent
indentcontent\n")
}
children.forEach {
it.render(builder, "$indent ")
}
builder.append(“
i
n
d
e
n
t
<
/
indent
indent\n”)
return builder.toString()
}
}
4-2-3 定義各個(gè)子節(jié)點(diǎn):
// 這是 HTML 最外層的標(biāo)簽:
class HTML : BaseElement(“html”) {
fun head(block: Head.() -> Unit): Head {
val head = Head()
head.block()
this.children += head
return head
}
fun body(block: Body.() -> Unit): Body {
val body = Body()
body.block()
this.children += body
return body
}
}
// 接著是 標(biāo)簽
class Head : BaseElement(“head”) {
fun title(block: () -> String): Title {
val content = block()
val title = Title(content)
this.children += title
return title
}
}
// 這是 Head 里面的 title 標(biāo)簽
class Title(content: String) : BaseElement(“title”, content)
// 然后是 標(biāo)簽
class Body : BaseElement(“body”) {
fun h1(block: () -> String): H1 {
val content = block()
val h1 = H1(content)
this.children += h1
return h1
}
fun p(block: () -> String): P {
val content = block()
val p = P(content)
this.children += p
return p
}
fun img(src: String, alt: String): IMG {
val img = IMG().apply {
this.src = src
this.alt = alt
}
this.children += img
return img
}
}
// 剩下的都是 body 里面的標(biāo)簽
class P(content: String) : BaseElement(“p”, content)
class H1(content: String) : BaseElement(“h1”, content)
class IMG : BaseElement(“img”) {
var src: String
get() = hashMap[“src”]!!
set(value) {
hashMap[“src”] = value
}
var alt: String
get() = hashMap[“alt”]!!
set(value) {
hashMap[“alt”] = value
}
// 拼接 標(biāo)簽
override fun render(builder: StringBuilder, indent: String): String {
builder.append(“
i
n
d
e
n
t
<
indent<
indent builder.append(renderAttributes()) builder.append(" /$name>\n") return builder.toString() } private fun renderAttributes(): String { val builder = StringBuilder() for ((attr, value) in hashMap) { builder.append(" KaTeX parse error: Expected group as argument to '\"' at end of input: attr=\"value"") } return builder.toString() } } 4-2-4 定義輸出 HTML 代碼的方法 fun html(block: HTML.() -> Unit): HTML { val html = HTML() html.block() return html } 4-3 HTML 代碼展示 class WebActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_web) val myWebView: WebView = findViewById(R.id.webview) myWebView.loadDataWithBaseURL(null, getHtmlStr(), “text/html”, “UTF-8”, null); } private fun getHtmlStr(): String { return html { head { title { “Kotlin Jetpack In Action” } } body { 自我介紹一下,小編13年上海交大畢業(yè),曾經(jīng)在小公司待過,也去過華為、OPPO等大廠,18年進(jìn)入阿里一直到現(xiàn)在。 深知大多數(shù)初中級Android工程師,想要提升技能,往往是自己摸索成長或者是報(bào)班學(xué)習(xí),但對于培訓(xùn)機(jī)構(gòu)動(dòng)則近萬的學(xué)費(fèi),著實(shí)壓力不小。自己不成體系的自學(xué)效果低效又漫長,而且極易碰到天花板技術(shù)停滯不前! 因此收集整理了一份《2024年Android移動(dòng)開發(fā)全套學(xué)習(xí)資料》,初衷也很簡單,就是希望能夠幫助到想自學(xué)提升又不知道該從何學(xué)起的朋友,同時(shí)減輕大家的負(fù)擔(dān)。 既有適合小白學(xué)習(xí)的零基礎(chǔ)資料,也有適合3年以上經(jīng)驗(yàn)的小伙伴深入學(xué)習(xí)提升的進(jìn)階課程,基本涵蓋了95%以上Android開發(fā)知識點(diǎn),真正體系化! 由于文件比較大,這里只是將部分目錄截圖出來,每個(gè)節(jié)點(diǎn)里面都包含大廠面經(jīng)、學(xué)習(xí)筆記、源碼講義、實(shí)戰(zhàn)項(xiàng)目、講解視頻,并且會(huì)持續(xù)更新! 如果你覺得這些內(nèi)容對你有幫助,可以掃碼獲取?。。▊渥ⅲ篈ndroid) 最后我想說 為什么很多程序員做不了架構(gòu)師? 1、良好健康的職業(yè)規(guī)劃很重要,但大多數(shù)人都忽略了 2、學(xué)習(xí)的習(xí)慣很重要,持之以恒才是正解。 3、編程思維沒能提升一個(gè)臺階,局限在了編碼,業(yè)務(wù),沒考慮過選型、擴(kuò)展 4、身邊沒有好的架構(gòu)師引導(dǎo)、培養(yǎng)。所處的圈子對程序員的成長影響巨大。 金九銀十面試季,跳槽季,整理面試題已經(jīng)成了我多年的習(xí)慣!在這里我和身邊一些朋友特意整理了一份快速進(jìn)階為Android高級工程師的系統(tǒng)且全面的學(xué)習(xí)資料。涵蓋了Android初級——Android高級架構(gòu)師進(jìn)階必備的一些學(xué)習(xí)技能。 附上:我們之前因?yàn)榍镎惺占亩滓欢€互聯(lián)網(wǎng)公司Android面試真題(含BAT、小米、華為、美團(tuán)、滴滴)和我自己整理Android復(fù)習(xí)筆記(包含Android基礎(chǔ)知識點(diǎn)、Android擴(kuò)展知識點(diǎn)、Android源碼解析、設(shè)計(jì)模式匯總、Gradle知識點(diǎn)、常見算法題匯總。) 里面包含不同方向的自學(xué)編程路線、面試題集合/面經(jīng)、及系列技術(shù)文章等,資源持續(xù)更新中… 《Android學(xué)習(xí)筆記總結(jié)+移動(dòng)架構(gòu)視頻+大廠面試真題+項(xiàng)目實(shí)戰(zhàn)源碼》,點(diǎn)擊傳送門即可獲取! BmxX-1712287928320)] [外鏈圖片轉(zhuǎn)存中…(img-FPsDfFrP-1712287928320)] [外鏈圖片轉(zhuǎn)存中…(img-zYsu8usP-1712287928321)] [外鏈圖片轉(zhuǎn)存中…(img-FJN4FJGz-1712287928321)] [外鏈圖片轉(zhuǎn)存中…(img-sXBHHNte-1712287928322)] 既有適合小白學(xué)習(xí)的零基礎(chǔ)資料,也有適合3年以上經(jīng)驗(yàn)的小伙伴深入學(xué)習(xí)提升的進(jìn)階課程,基本涵蓋了95%以上Android開發(fā)知識點(diǎn),真正體系化! 由于文件比較大,這里只是將部分目錄截圖出來,每個(gè)節(jié)點(diǎn)里面都包含大廠面經(jīng)、學(xué)習(xí)筆記、源碼講義、實(shí)戰(zhàn)項(xiàng)目、講解視頻,并且會(huì)持續(xù)更新! 如果你覺得這些內(nèi)容對你有幫助,可以掃碼獲?。。。▊渥ⅲ篈ndroid) 最后我想說 為什么很多程序員做不了架構(gòu)師? 1、良好健康的職業(yè)規(guī)劃很重要,但大多數(shù)人都忽略了 2、學(xué)習(xí)的習(xí)慣很重要,持之以恒才是正解。 3、編程思維沒能提升一個(gè)臺階,局限在了編碼,業(yè)務(wù),沒考慮過選型、擴(kuò)展 4、身邊沒有好的架構(gòu)師引導(dǎo)、培養(yǎng)。所處的圈子對程序員的成長影響巨大。 金九銀十面試季,跳槽季,整理面試題已經(jīng)成了我多年的習(xí)慣!在這里我和身邊一些朋友特意整理了一份快速進(jìn)階為Android高級工程師的系統(tǒng)且全面的學(xué)習(xí)資料。涵蓋了Android初級——Android高級架構(gòu)師進(jìn)階必備的一些學(xué)習(xí)技能。 附上:我們之前因?yàn)榍镎惺占亩滓欢€互聯(lián)網(wǎng)公司Android面試真題(含BAT、小米、華為、美團(tuán)、滴滴)和我自己整理Android復(fù)習(xí)筆記(包含Android基礎(chǔ)知識點(diǎn)、Android擴(kuò)展知識點(diǎn)、Android源碼解析、設(shè)計(jì)模式匯總、Gradle知識點(diǎn)、常見算法題匯總。) [外鏈圖片轉(zhuǎn)存中…(img-vaGe26iB-1712287928322)] 里面包含不同方向的自學(xué)編程路線、面試題集合/面經(jīng)、及系列技術(shù)文章等,資源持續(xù)更新中… 《Android學(xué)習(xí)筆記總結(jié)+移動(dòng)架構(gòu)視頻+大廠面試真題+項(xiàng)目實(shí)戰(zhàn)源碼》,點(diǎn)擊傳送門即可獲?。?/p> 柚子快報(bào)邀請碼778899分享: 精彩鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。