Kotlin基礎(chǔ)2-進(jìn)階入門

前言

上一節(jié)簡(jiǎn)單了解了 Kotlin 的語(yǔ)法使用错忱,對(duì)一些語(yǔ)法有一些了解后,在面對(duì) Java 轉(zhuǎn) Kotlin 時(shí)或想了解 Kotlin 到底轉(zhuǎn)成了什么樣的 Java?那我們需要知道這兩個(gè)技能良蛮,不僅能偷一些懶也能了解一些簡(jiǎn)單原理掖鱼;

  1. AndroidStudio 中Code → Convert Java File to Kotlin File雙擊 Shift 輸入 Convert Java File to Kotlin File

    一鍵 Java 轉(zhuǎn) Kotlin然走,無(wú)法從 Kotlin 再轉(zhuǎn)回 Java 哦。雖然能偷懶戏挡,但不能過(guò)分依賴芍瑞,前提你要了解 Kotlin,轉(zhuǎn)的時(shí)候有一些實(shí)現(xiàn)與性能會(huì)有出入褐墅。

  2. 雙擊 Shift 輸入 Show Kotlin Bytecode

    Kotlin 有挺多 Java 中沒(méi)有的特性拆檬,如果你想看下 Kotlin 這樣實(shí)現(xiàn)在 Java 中到底是怎樣體現(xiàn)的,那你可以去瞧一瞧掌栅。

構(gòu)造器

上一節(jié)簡(jiǎn)單上手接觸了 Kotlin 的一些語(yǔ)法秩仆,我們用上一節(jié)的知識(shí)知道如何實(shí)現(xiàn)一個(gè)類的構(gòu)造器。

class KotlinView : View {
    private var paint = Paint() // 初始化畫筆
    // 次級(jí)構(gòu)造器 → 調(diào)用 this(主構(gòu)造器)
    constructor(context: Context) : this(context, null)
    // 主構(gòu)造器 → 調(diào)用 super
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
      // 畫筆樣式設(shè)置  
      paint.style = Paint.Style.STROKE
    }
}

Kotlin 在構(gòu)造器還有更簡(jiǎn)潔的寫法猾封,主要有主構(gòu)造器澄耍、**次級(jí)構(gòu)造 **以及 init代碼塊構(gòu)造屬性

主構(gòu)造器

主構(gòu)造器是什么呢晌缘?

我認(rèn)為在一個(gè)類里面最終消費(fèi)的構(gòu)造器就是主構(gòu)造器齐莲;比如上面的代碼里面 constructor(context: Context)調(diào)用的是this,最終流向的還是constructor(context: Context, attrs: AttributeSet?)磷箕,所以它就是主構(gòu)造器选酗。

那主構(gòu)造器如何更簡(jiǎn)潔呢?

可以將主構(gòu)造器的函數(shù)直接放置類聲明的后面就行岳枷∶⑻睿看下簡(jiǎn)約后的代碼

class KotlinView constructor(context: Context, attrs: AttributeSet?) : View(context, attrs) {
    // 可以訪問(wèn)到主構(gòu)造函數(shù) context 參數(shù)
    val color = context.getColor(R.color.white)
    private var paint = Paint()
    constructor(context: Context) : this(context, null)
        // paint.style = Paint.Style.STROKE
    fun testParams(){
        context.resources// 不報(bào)紅,但實(shí)際 context 并不是主構(gòu)造函數(shù)里面的參數(shù)
      attrs// 會(huì)報(bào)紅空繁,意味著訪問(wèn)不到
    }
}

倒是簡(jiǎn)潔了蠻多殿衰,可是我們主構(gòu)造器里面的邏輯咋辦?別慌盛泡,往下看闷祥!

另外:

  1. 如果主構(gòu)造器沒(méi)有被可見(jiàn)性修飾符注解標(biāo)記,那么constructor是可以省略的傲诵;
  2. 還有成員變量初始化時(shí)是可以直接訪問(wèn)主構(gòu)造參數(shù)凯砍,例如color變量。注意點(diǎn)看代碼!

次級(jí)構(gòu)造

這個(gè)就不多講了,除了主構(gòu)造器,剩下的不就是次級(jí)構(gòu)造了嗎

init 代碼塊

在上面我們把主構(gòu)造器簡(jiǎn)化了,可是里面的邏輯沒(méi)地方放了摘完,現(xiàn)在就改init上場(chǎng)了

class KotlinView (context: Context, attrs: AttributeSet?) : View(context, attrs) {
    private var paint = Paint()
    constructor(context: Context) : this(context, null)
    init {
        paint.style = Paint.Style.STROKE
    }
}

這樣就把主構(gòu)造器里面的邏輯安置好了,值得注意的一點(diǎn):在初始化的時(shí)候初始化塊會(huì)按照它們?cè)?strong>文件中出現(xiàn)的順序執(zhí)行枉侧。如果paint變量在init的下面聲明汞幢,那paintinit里面就會(huì)報(bào)紅钳榨,在編譯的時(shí)候,init代碼會(huì)按照文件出現(xiàn)的順序插入到實(shí)際的構(gòu)造函數(shù)中纽门,聲明放在init下面的話薛耻,那就會(huì)被插入在paint.style的下面。

PS:注意到主構(gòu)造器的`constructor沒(méi)赏陵?沒(méi)見(jiàn)了是吧饼齿,因?yàn)樗葲](méi)有被可見(jiàn)性修飾也沒(méi)有被注解,所以是可以省略的蝙搔。

構(gòu)造屬性

構(gòu)造屬性是什么呢缕溉,我給他翻譯過(guò)來(lái)就是:構(gòu)造函數(shù)的參數(shù)屬性化。

常規(guī)保守寫法是這樣的

class KotlinInfo {
    var name: String? = null
    var age: Int? = null

    constructor(name: String?, age: Int?) {
        this.name = name
        this.age = age
    }
}

可是代碼太多吃型,不是 Kotlin 的風(fēng)格证鸥,那我們這樣來(lái)

// 簡(jiǎn)化方式一:直接使用主構(gòu)造函數(shù)參數(shù)初始化。
class KotlinInfo constructor(name: String?, age: Int?) {
    var name: String? = name
    var age: Int? = age
}
// 簡(jiǎn)化方式二:emmm~還是不夠簡(jiǎn)潔勤晚,再來(lái)
class KotlinInfo constructor(var name: String?, var age: Int?) {
}

是吧枉层,這樣才對(duì),主構(gòu)造函數(shù)的入?yún)⒓词菂?shù)又變量赐写,所以這就是構(gòu)造屬性鸟蜡。

data class

在 Java 中重寫hashCode()toString()等類時(shí)挺邀,需要我們一個(gè)個(gè)去重寫出來(lái)揉忘,在 Kotlin 中我們只需要在class前聲明為data「數(shù)據(jù)類」就會(huì)自動(dòng)為我們生成hashCode()toString()端铛、equals()泣矛、copy()componentN()方法沦补。

// 聲明類為 data 類型
data class KotlinInfo constructor(var name: String?, var age: Int?) {}

fun main() {
    val kotlinInfo = KotlinInfo("Kotlin", 25)
    kotlinInfo.hashCode()
    kotlinInfo.toString()
    kotlinInfo.equals(null)
    kotlinInfo.copy()// 淺拷貝
    kotlinInfo.component1()// 對(duì)應(yīng)第一個(gè)變量 name
    kotlinInfo.component2()// 對(duì)應(yīng)第二個(gè)變量 age
}

但是生成數(shù)據(jù)類還是有前提的

  • 主構(gòu)造函數(shù)需要至少有一個(gè)參數(shù)
  • 主構(gòu)造函數(shù)的所有參數(shù)需要標(biāo)記為 valvar乳蓄;相當(dāng)于必須是構(gòu)造屬性
  • 數(shù)據(jù)類不能是抽象、開(kāi)放夕膀、密封或者內(nèi)部的

component屬性的順序?qū)?yīng)主構(gòu)造參數(shù)的聲明順序

相等性

  • == 結(jié)構(gòu)相等虚倒,調(diào)用了equals()
  • === 引用相等

注意:與 Java 不同

解構(gòu)

Kotlin 的一大特點(diǎn)解構(gòu)聲明,表示一個(gè)對(duì)象一次賦值給多個(gè)變量产舞。

data class KotlinInfo constructor(var name: String?, var age: Int?) {}

fun main() {
    val (name, age) = KotlinInfo("Kotlin", 25)
}

這個(gè)就有一個(gè)前提魂奥,必須是數(shù)據(jù)類才能做解構(gòu)聲明,當(dāng)然不是數(shù)據(jù)類我們也可以自定義解構(gòu)聲明易猫,比如:

class KotlinInfo constructor(var name: String?, var age: Int?) {
    operator fun component1(): String? {
        return name
    }

    operator fun component2(): Int? {
        return age
    }
}

fun main() {
    val (name, age) = KotlinInfo("Kotlin", 25)
}

Elvis 操作符

通過(guò)?:操作簡(jiǎn)化if null的操作

// kotlinInfo.name 為空返回"沒(méi)有姓名"
val name = kotlinInfo.name ?: "沒(méi)有姓名"
// kotlinInfo.age 為空提前返回
val age = kotlinInfo.age ?: return
// kotlinInfo.age 為空拋出異常
val age = kotlinInfo.age ?: thorw IllegalArgumentException("age is null") 
// 對(duì)應(yīng) if 代碼是這樣的
var name = kotlinInfo.name
if (kotlinInfo.name == null){
    name = "沒(méi)有姓名"
}

when 操作符

when不僅條件分支上支持條件表達(dá)式耻煤,分支上也可以多個(gè)值,通過(guò),分割;還可以有返回值哈蝇;默認(rèn)分支條件都是布爾表達(dá)式棺妓。

// num 大于 0 哦~
val str = when (num) {
    in 0..99 -> "< 100"http:// 大于等于 0 小于100;返回字符串
    100 -> "100"http:// 等于 100炮赦;返回字符串
    200 -> "200"http:// 等于 200怜跑;返回字符串
    300, 301 -> "300,301"http:// 等于 300 或 301;返回字符串
    else -> ">300"http:// 其他情況全部返回字符串
}

operator

一般用的時(shí)候找下就行吠勘,與 Java 的大致差不多性芬,列舉下加減乘除

操作符 函數(shù)
a+b a.plus(b)
a-b a.minus(b)
a*b a.times(b)
a/b a.div(b)

Lambda

Lambda 在 Java 中也接觸過(guò),這里主要說(shuō)下哪些可以省略

val kotlinInfos: List<KotlinInfo> = ArrayList()
// 1剧防、常規(guī)寫法
for (kotlinInfo in kotlinInfos) {
    if (kotlinInfo.name.equals("Kotlin")) {
        return
    }
}
// 2植锉、使用 Lambda 的常規(guī)寫法
kotlinInfos.forEach({ kotlinInfo: KotlinInfo ->
    if (kotlinInfo.name.equals("Kotlin")) {
        return
    }
})
// 3、函數(shù)最后一個(gè)參數(shù)是 Lambda 峭拘,那么可以將 Lambda 表達(dá)式移至外面俊庇,如下:
kotlinInfos.forEach() { kotlinInfo: KotlinInfo ->
    if (kotlinInfo.name.equals("Kotlin")) {
        return
    }
}
// 4.1、如果傳入?yún)?shù)只有一個(gè) Lambda鸡挠,小括號(hào)可以省略暇赤;forEach()
// 4.2、Lamdda 表達(dá)式只有一個(gè)參數(shù)宵凌,類型也可以省略鞋囊;kotlinInfo: KotlinInfo
kotlinInfos.forEach{ kotlinInfo ->
    if (kotlinInfo.name.equals("Kotlin")) {
        return
    }
}
// 5、Lambda 表達(dá)式只有一個(gè)參數(shù)還可以通過(guò)隱式的 it 來(lái)訪問(wèn)參數(shù)瞎惫,如下:
kotlinInfos.forEach {
    if (it.name.equals("Kotlin")) {
        return
    }
}

循環(huán)

repeat(100){
    // do something
}
for (i in 0..99){
    // do something
}
for (i in 0 until 100) {
    // do something
}

infix

前提:

  • 必須是成員函數(shù)擴(kuò)展函數(shù)溜腐;
  • 必須只能接收一個(gè)參數(shù)且不能有默認(rèn)值

下面是until的源碼瓜喇,函數(shù)是Int的擴(kuò)展函數(shù)且只有一個(gè)參數(shù)無(wú)默認(rèn)值

public infix fun Int.until(to: Int): IntRange {
    if (to <= Int.MIN_VALUE) return IntRange.EMPTY
    return this .. (to - 1).toInt()
}

嵌套函數(shù)

Kotlin 的函數(shù)中是可以在嵌套函數(shù)的挺益,而且在內(nèi)部函數(shù)里面可以訪問(wèn)外部函數(shù)的參數(shù);但是有一點(diǎn)需要注意乘寒,每次調(diào)用外部函數(shù)時(shí)不僅會(huì)產(chǎn)生一個(gè)外部函數(shù)的對(duì)象而且內(nèi)部函數(shù)也會(huì)產(chǎn)生一個(gè)函數(shù)對(duì)象望众,所以用的時(shí)候需要謹(jǐn)慎。

fun external(string: String) {
    fun internal() {
        print(string)
    }
    print(string)
}

函數(shù)簡(jiǎn)化

var string: String? = null
fun printText() {
    print(string)
}
fun getText(): String? {
    return string
}
var string: String? = null
fun printText() = print(string)
fun getText(): String? = string

函數(shù)參數(shù)默認(rèn)值

fun toast(context: Context, text: CharSequence) {
    toast(context, text, Toast.LENGTH_SHORT)
}
fun toast(context: Context, text: CharSequence, duration: Int) {
    Toast.makeText(context, text, duration).show()
}

fun toast(context: Context, text: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
        Toast.makeText(context, text, duration).show()
}

在 Java 中調(diào)用就只能使用兩個(gè)參數(shù)的方法伞辛,這時(shí)候就需要用到@JvmOverloads

@JvmOverloads

toast函數(shù)上標(biāo)記@JvmOverloads注解后烂翰,Java 就能用了

擴(kuò)展

// 正常寫法
fun dp2px(dx: Float): Float { ...}
// 擴(kuò)展寫法
fun Float.dp2px(): Float { ...}
// 兩者使用區(qū)別
dp2px(10f)
10f.dp2px()

函數(shù)類型

函數(shù)類型傳入?yún)?shù)類型返回值類型組成,傳入?yún)?shù)需要用()蚤氏,用->連接返回值甘耿,返回值為Unit不可省略;

函數(shù)類型實(shí)際是一個(gè)接口竿滨,傳遞函數(shù)的時(shí)候可通過(guò)::函數(shù)名佳恬、匿名函數(shù)或者使用lambda

class View {
    interface OnClickListener {
        fun OnClick(view: View)
    }

    fun setOnClickListener(listener: (View) -> Unit) { }
}

fun onClick(view: View) {
    print("click")
}

fun main() {
    val view = View()
    // 方式一:函數(shù)傳入
    view.setOnClickListener(::onClick)
    // 方式二:匿名函數(shù)
    view.setOnClickListener(fun(view: View) { print("click") })
    // 方式三:Lambda
    view.setOnClickListener { print("click") }
}

內(nèi)聯(lián)函數(shù)

內(nèi)聯(lián)函數(shù)

inline關(guān)鍵字聲明內(nèi)聯(lián)函數(shù)捏境,編譯時(shí)內(nèi)聯(lián)函數(shù)的函數(shù)體會(huì)被插入至調(diào)用處,因此實(shí)現(xiàn)內(nèi)聯(lián)函數(shù)時(shí)注意毁葱,函數(shù)體內(nèi)的代碼行數(shù)盡量減少垫言。

內(nèi)聯(lián)函數(shù)有什么作用呢?看看代碼

inline fun log(text: String) {
  Log.d("TAG", text)
}
fun main() {
  log("log text")
}
// 用 inline 修飾聲明內(nèi)聯(lián)函數(shù) log
// 實(shí)際編譯后的代碼就變成這樣了
fun main() {
  log.d("TAG", "log text")
}
// 將內(nèi)聯(lián)函數(shù) log 里面的代碼直接放到了調(diào)用的地方
// 到底有什么用呢倾剿?
// 從調(diào)用棧的角度來(lái)看看
// 不使用內(nèi)聯(lián)函數(shù)大致是這樣的
// main 入棧 → log 入棧 → log.d() → log 出棧 → main 出棧
// 使用內(nèi)聯(lián)函數(shù)是這樣的
// main 入棧 → log.d() → main 出棧

所以內(nèi)聯(lián)函數(shù)能減少一層調(diào)用棧骏掀,但是如果函數(shù)體里面內(nèi)容較多時(shí),編譯器會(huì)變得比較辛苦柱告,因此我們?cè)趯?shí)現(xiàn)內(nèi)聯(lián)函數(shù)時(shí)一定要注意。

思考:示例代碼里面的內(nèi)聯(lián)函數(shù)loginline有警告笑陈!

部分禁用內(nèi)聯(lián)

noinline可以禁止部分參數(shù)參與編譯

具體化的類型參數(shù)

通過(guò)配合inline + reified達(dá)到真泛型的效果

interface Api{ ... }
val retrofit = Retrofit.Builder()
        .baseUrl("https://api.com")
        .build()

inline fun <reified T> create(): T {
    return retrofit.create(T::class.java)
}

val api = create<Api>()

委托

屬性委托

常見(jiàn)的屬性操作际度,通過(guò)委托的方式,讓其只實(shí)現(xiàn)一次

  • lazy:延遲屬性涵妥,值僅在首次訪問(wèn)時(shí)計(jì)算
  • observable:可觀察屬性:屬性發(fā)生改變時(shí)通知

類委托

通過(guò)類委托的模式減少繼承

標(biāo)準(zhǔn)函數(shù)

  • apply
    • 返回自身:作用域中使用this做為參數(shù)
    • 適合對(duì)一個(gè)對(duì)象做附加操作
  • also
    • 返回自身:作用域中使用it做為參數(shù)
  • run
    • 無(wú)需返回自身:作用域中使用this做為參數(shù)
  • let
    • 無(wú)需返回自身:作用域中使用it做為參數(shù)
    • 適合配合空判斷時(shí)
  • with
    • 使用對(duì)同一個(gè)對(duì)象進(jìn)行多次操作時(shí)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末乖菱,一起剝皮案震驚了整個(gè)濱河市蓬网,隨后出現(xiàn)的幾起案子窒所,更是在濱河造成了極大的恐慌帆锋,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锯厢,死亡現(xiàn)場(chǎng)離奇詭異皮官,居然都是意外死亡实辑,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門剪撬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)摄乒,“玉大人,你說(shuō)我怎么就攤上這事残黑。” “怎么了挤茄?”我有些...
    開(kāi)封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵冰木,是天一觀的道長(zhǎng)笼恰。 經(jīng)常有香客問(wèn)我歇终,道長(zhǎng),這世上最難降的妖魔是什么追葡? 我笑而不...
    開(kāi)封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任宜肉,我火速辦了婚禮翎碑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘日杈。我一直安慰自己莉擒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布填硕。 她就那樣靜靜地躺著鹿鳖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪恋拍。 梳的紋絲不亂的頭發(fā)上藕甩,一...
    開(kāi)封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音狭莱,去河邊找鬼腋妙。 笑死,一個(gè)胖子當(dāng)著我的面吹牛骤素,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播痕檬,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼梦谜,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了闭树?” 一聲冷哼從身側(cè)響起荒澡,我...
    開(kāi)封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤仰猖,失蹤者是張志新(化名)和其女友劉穎奈籽,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體躏升,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡狼忱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年钻弄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片饲帅。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瘤泪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出赦邻,到底是詐尸還是另有隱情实檀,我是刑警寧澤按声,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布儒喊,位于F島的核電站币呵,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏芯义。R本人自食惡果不足惜妻柒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一举塔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧央渣,春花似錦、人聲如沸北启。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至泳猬,卻和暖如春暂殖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背呛每。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工晨横, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留箫柳,地道東北人啥供。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓伙狐,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親贷屎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容