Kotlin基礎(chǔ)

Attention:

  • 本文章適用于有編程基礎(chǔ)的人石洗,如果有多種編程語(yǔ)言基礎(chǔ),例如Swift紧显,會(huì)發(fā)現(xiàn)很多相似之處讲衫,學(xué)習(xí)起來(lái)也會(huì)比較容易
  • 本文章屬于筆記,沒(méi)有知識(shí)講解條理孵班,不太適合透徹學(xué)習(xí)Kotlin涉兽,純屬寫代碼時(shí)的參考

一、基礎(chǔ)


基本內(nèi)置數(shù)據(jù)類型

  • String
  • Char
  • Boolean --> Java boolean
  • Int --> Java int
  • Double --> Java double
  • Float --> Java float
  • List
  • Set
  • Map
    Kotlin中一些引用類型編譯器會(huì)轉(zhuǎn)化為Java的基本類型篙程,不會(huì)有額外的性能消耗

編譯時(shí)常量

const val PI = 3.1415
  • 編譯時(shí)常量只能是常用的基本數(shù)據(jù)類型:(String, Double, Int, Float, Long, Short, Byte, Char, Boolean)
  • 編譯時(shí)常量只能定義在函數(shù)外(如果在函數(shù)內(nèi)定義的話就必須在運(yùn)行時(shí)才能調(diào)用函數(shù)賦值枷畏,就不能稱是編譯時(shí)常量了,所以編譯時(shí)常量只能定義在函數(shù)之外虱饿,這樣就能在編譯期間就初始化了)

range表達(dá)式

image.png

when表達(dá)式

表達(dá)式有返回值拥诡,語(yǔ)句沒(méi)有返回值

    val week = 5
    val info = when(week) {
        1 -> "今天是星期1"
        2 -> "今天是星期2"
        3 -> "今天是星期3"
        4 -> "今天是星期4"
        5 -> "今天是星期5"
//        6 -> true        // when返回Any類型
        else -> "今天是周末" // when返回String類型
//        else -> {        // when返回Any類型
//            println("Any")
//        }
    }
    println(info)

String模版

    val park = "黃石公園"
    val time = 6
    println("今天去${park}玩了$time 個(gè)小時(shí)") // $變量名 后面沒(méi)有字符串(標(biāo)點(diǎn)符號(hào)、空格可以)的可以不加{}

    val isLogin = true
    println("${if (isLogin) "登錄成功" else "登錄失敗氮发,請(qǐng)重試"}")

函數(shù)

image.png

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

fun action1(name: String, age: Int) {
    println("姓名渴肉;$name, 年齡:$age")
}

fun action2(name: String, age: Int = 88) {
    println("姓名;$name, 年齡:$age")
}

具名函數(shù)參數(shù)

fun main() {
    loginAction(age = 30, name = "test", phone = "12345", pwd = "123")
}

fun loginAction(name: String, pwd: String, age: Int, phone: String) {
    println("name: $name, pwd: $pwd, age: $age, phone: $phone")
}

Unit函數(shù)特點(diǎn)

image.png

Nothing類型特點(diǎn)

TODO() // 終止程序繼續(xù)執(zhí)行爽冕,拋出一個(gè)異常

fun main() {
    show(-1)
}

private fun show(number: Int) {
    when(number) {
        -1 -> TODO("分?jǐn)?shù)不合法")
        in 0..59 -> println("不及格")
        in 60..70 -> println("及格")
        in 71..100 -> println("優(yōu)秀")
    }
}

帶反引號(hào)函數(shù)名特點(diǎn)

fun main() {
    // 第一種情況:函數(shù)特殊命名
    `登錄功能測(cè)試 需求實(shí)現(xiàn)人張三`("test", "pwd")

    // 第二種情況:函數(shù)命名使用了Kotlin中的關(guān)鍵詞
    JavaTest.`is`()
    JavaTest.`in`()

    // 第三種情況:函數(shù)名違反命名規(guī)則仇祭,反編譯時(shí)導(dǎo)致崩潰防止反編譯
    `655435383`()
}

private fun `655435383`() {
}

// 文檔中注釋
// 防止反編譯的函數(shù)`655435383`,作用是xxx

二颈畸、語(yǔ)法糖


匿名函數(shù)

fun main() {
    val len = "test".count()
    println(len) // 4
    
    val len2 = "test".count { 
        it == 't'
    }
    println(len2) // 2
}

匿名函數(shù)-函數(shù)類型&隱式返回

fun main() {
    // 1.函數(shù)輸入輸出的聲明
    val methodAction: () -> String

    // 2.對(duì)上面聲明函數(shù)的實(shí)現(xiàn)
    methodAction = {
        val name = "name"
        "返回值:$name" //最后一行就是函數(shù)的返回值
    }

    // 3.調(diào)用
    println(methodAction())
}

匿名函數(shù)-函數(shù)參數(shù)

fun main() {
    val methodAction: (Int, Int, Int) -> String = { number1, number2, number3 ->
        val name = "name"
        "$name: $number1, $number2, $number3"
    }
    println(methodAction(10, 20, 30))
}

匿名函數(shù)-it關(guān)鍵字

匿名函數(shù)的參數(shù)只有一個(gè)的話乌奇,會(huì)自動(dòng)生成參數(shù)名it,代表被傳入的參數(shù)對(duì)象

fun main() {
    val methodAction: (String) -> String = {
        "$it"
    }
    println(methodAction("name"))
    
    val methodAction2: (Double) -> String = {
        "$it"
    }
    println(methodAction2(123.4))
}

匿名函數(shù)-類型推斷

fun main() {
    val method1 = { v1: Double, v2: Float, v3: Int ->
        "v1:$v1, v2:$v2, v3:$v3"
    } // (Double, Float, Int) -> String
    println(method1(12.3, 45.6f, 10))

    val method2 = {
        123.0f
    } // () -> Unit
    println(method2())

    val method3 = { number: Int ->
        number
    } // (Int) -> Int
    println(method3(1))
}

在函數(shù)中定義參數(shù)是函數(shù)的參數(shù)

fun main() {
    loginApi("name", "pwdd") { msg: String, code: Int ->
        println("msg: $msg, code: $code")
    }
}

const val USER_NAME = "name"
const val USER_PWD = "pwdd"

fun loginApi(username: String?, userpwd: String?, responseResult: (String, Int) -> Unit) {
    if (username == null || userpwd == null) {
        TODO("用戶名或密碼為null")
    }

    if (username.length > 3 && userpwd.length > 3) {
        if (webLoginApi(username, userpwd)) {
            responseResult("成功", 200)
        } else {
            responseResult("失敗", 400)
        }
    } else {
        TODO("用戶名或密碼不合格")
    }
}

private fun webLoginApi(name: String?, pwd: String?): Boolean {
    return name == USER_NAME && pwd == USER_PWD
}

簡(jiǎn)略寫法

fun main() {
    // 第一種方式
    loginApi("name", "pwdd", { msg: String, code: Int ->
        println("msg: $msg, code: $code")
    })
    
    // 第二種方式
    loginApi("name", "pwdd", responseResult = { msg: String, code: Int ->
        println("msg: $msg, code: $code")
    })
    
    // 第三種方式眯娱,簡(jiǎn)略寫法
    loginApi("name", "pwdd") { msg: String, code: Int ->
        println("msg: $msg, code: $code")
    }
}

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

inline
如果函數(shù)使用lambda做為參數(shù)礁苗,需要聲明成內(nèi)聯(lián),否則在調(diào)用時(shí)會(huì)生成多個(gè)對(duì)象來(lái)完成lambda的調(diào)用困乒,引起性能損耗寂屏。
如果函數(shù)使用內(nèi)聯(lián),相當(dāng)于C++的#define 宏定義 宏替換,會(huì)把代碼替換到調(diào)用處迁霎,沒(méi)有任何新建函數(shù)吱抚、新建對(duì)象的損耗

fun main() {
    loginApi("name", "pwdd") { msg: String, code: Int ->
        println("msg: $msg, code: $code")
    }
}

const val USER_NAME = "name"
const val USER_PWD = "pwdd"

inline fun loginApi(username: String?, userpwd: String?, responseResult: (String, Int) -> Unit) {
    if (username == null || userpwd == null) {
        TODO("用戶名或密碼為null")
    }

    if (username.length > 3 && userpwd.length > 3) {
        if (webLoginApi(username, userpwd)) {
            responseResult("成功", 200)
        } else {
            responseResult("失敗", 400)
        }
    } else {
        TODO("用戶名或密碼不合格")
    }
}

fun webLoginApi(name: String?, pwd: String?): Boolean {
    return name == USER_NAME && pwd == USER_PWD
}

函數(shù)引用

lambda屬于函數(shù)類型的 對(duì)象,在使用函數(shù)作為參數(shù)時(shí)考廉,需要把普通函數(shù)通過(guò) :: 轉(zhuǎn)為函數(shù)類型的對(duì)象

fun main() {
    login("name", "pwdd", ::printResult)
    // 或
    val obj = ::printResult
    login("name", "pwdd", obj)
}

const val USER_NAME = "name"
const val USER_PWD = "pwdd"

fun printResult(msg: String, code: Int) {
    println("結(jié)果:msg: $msg, code: $code")
}

inline fun login(username: String, pwd: String, responseResult: (String, Int) -> Unit) {
    if (username == USER_NAME && pwd == USER_PWD) {
        responseResult("成功", 200)
    } else {
        responseResult("失敗", 400)
    }
}

使用函數(shù)做為返回類型

fun main() {
    val result = showMethod("info")
    println(result("張三", 30))
}

fun showMethod(info: String): (String, Int) -> String {
    println("show info: $info")

    return { name: String, age: Int ->
        "我是匿名函數(shù):name: $name, age: $age"
    }
}

匿名函數(shù)與具名函數(shù)

fun main() {
    // 匿名函數(shù)
    showPersonInfo("張三", 30) {
        println("$it")
    }

    // 具名函數(shù)
    showPersonInfo("張三", 30, ::showResultImpl)
}

fun showResultImpl(info: String) {
    println(info)
}

fun showPersonInfo(name: String, age: Int, showResult: (String) -> Unit) {
    val str = "name: $name, age: $age"
    showResult(str)
}

三秘豹、Kotlin內(nèi)置函數(shù)


Kotlin可空性特點(diǎn)

?

image.png


安全調(diào)用操作符

?

image.png


帶let的安全調(diào)用

fun main() {
    var name: String? = null
    // name = "張三"

    val result = name?.let {
        // it == name 本身
        // 如果能執(zhí)行到這里,it 一定不為 null
        if (it.isBlank()) {
            "default"
        } else {
            "[$it]"
        }
    }
    println(result)  // null
}

非空斷言操作符

!!

image.png


空合并操作符

?:

image.png


異常處理與自定義異常

fun main() {
    try {
        var name: String? = null
        checkException(name)
        println(name!!.length)
    } catch (e: Exception) {
        println("$e")
    }
}

fun checkException(name: String?) {
    name ?: throw CustomException()
}

class CustomException: IllegalArgumentException("代碼不嚴(yán)謹(jǐn)")

先決條件函數(shù)

checkNotNull()
requireNotNull()
require()

image.png


substring

image.png

split

fun main() {
    val text = "A,B,C,D"
    val list = text.split(",")
    // 直接輸出
    println(list)
    // 解構(gòu)昌粤,C++中也有解構(gòu)
    val(v1, v2, v3, v4) = list
    println("v1:$v1, v2:$v2, v3:$v3, v4:$v4")
}

replace

fun main() {
    val sourcePwd = "ABCDEFGHI"
    val newPwd = sourcePwd.replace(Regex("[ACF]")) {
        when (it.value) {
            "A" -> "1"
            "C" -> "2"
            "F" -> "3"
            else -> it.value
        }
    }
    println(newPwd)
}

== 與 === 比較操作

== 值比較
=== 引用比較


數(shù)字類型的安全轉(zhuǎn)換函數(shù)

toXxxOrNull()

fun main() {
    val number = "888.8".toIntOrNull()
    println(number ?: "轉(zhuǎn)換錯(cuò)誤")
}

Double 轉(zhuǎn) Int

fun main() {
    println(123.4567.toInt())       // 123, 只保留整數(shù)
    println(123.4567.roundToInt())  // 123, 四舍五入
    println(123.5567.toInt())       // 123
    println(123.5567.roundToInt())  // 124
    val r = "%.2f".format(123.4567)
    println(r)                      // 123.46, 四舍五入
}

apply 內(nèi)置函數(shù)

1.匿名函數(shù)中自動(dòng)生成this參數(shù)既绕,代表對(duì)象本身
2.apply函數(shù)始終返回的是對(duì)象本身,一般用于鏈?zhǔn)秸{(diào)用

val file = File("文件路徑")
file.setExecutable(true)
file.setReadable(true)
println(file.readLine())
// 等價(jià)于
val file = File("文件路徑")
file.apply {
    // apply函數(shù)默認(rèn)生成一個(gè)代表當(dāng)前對(duì)象的this
    setExecutable(true)
}.apply {
    setReadable(true)
}.apply {
    println(readlnOrNull())
}

let 內(nèi)置函數(shù)

1.匿名函數(shù)中自動(dòng)生成it參數(shù)涮坐,代表對(duì)象本身
2.最后一行做為返回值

fun main() {
    val result = listOf(6, 4, 7, 9).let {
        // it == 對(duì)象本身
        // 最后一行做為返回值
        it.first() + it.first()
    }
    println(result)

    val result2 = getMethod(null)
    println(result2)
}

fun getMethod(value: String?): String {
    return value?.let {
        it
    } ?: "傳值為null"
}

run 內(nèi)置函數(shù)

1.匿名函數(shù)中自動(dòng)生成this參數(shù)凄贩,代表對(duì)象本身,一般用于值類型發(fā)生改變的鏈?zhǔn)秸{(diào)用
2.最后一行做為返回值

fun main() {
    val str = "12345"
    // run 與 具名函數(shù)
    str
        .run(::isLengthOk)
        .run(::showText)
        .run(::mapText)
        .run(::println)

    // run 與 匿名函數(shù)
    str
        .run {
            length > 3
        }
        .run {
            if (this) "長(zhǎng)度ok" else "長(zhǎng)度不夠"
        }
        .run {
            "[$this]"
        }
        .run {
            println(this)
        }
}

fun isLengthOk(str: String) = str.length > 3

fun showText(isLengthOK: Boolean) = if (isLengthOK) "長(zhǎng)度ok" else "長(zhǎng)度不夠"

fun mapText(showText: String) = "[$showText]"

with 內(nèi)置函數(shù)

1.匿名函數(shù)中自動(dòng)生成this參數(shù)袱讹,代表對(duì)象本身疲扎,一般用于值類型發(fā)生改變的鏈?zhǔn)秸{(diào)用
2.最后一行做為返回值
與 run 函數(shù)基本相同,只是調(diào)用方式不同

fun main() {
    val str = "12345"

    // with 與 具名函數(shù)
    val r1 = with(str, ::isLengthOk)
    val r2 = with(r1, :: showText)
    val r3 = with(r2, ::mapText)
    with(r3, ::println)

    // with 與 匿名函數(shù)
    val r11 = with(str) {
        length > 3
    }
    val r22 = with(r11) {
        if (this) "長(zhǎng)度ok" else "長(zhǎng)度不夠"
    }
    val r33 = with(r22) {
        "[$this]"
    }
    with(r33) {
        println(this)
    }
}

fun isLengthOk(str: String) = str.length > 3

fun showText(isLengthOK: Boolean) = if (isLengthOK) "長(zhǎng)度ok" else "長(zhǎng)度不夠"

fun mapText(showText: String) = "[$showText]"

also 內(nèi)置函數(shù)

1.匿名函數(shù)中自動(dòng)生成it參數(shù)捷雕,代表對(duì)象本身
2.also函數(shù)始終返回的是對(duì)象本身椒丧,一般用于鏈?zhǔn)秸{(diào)用

fun main() {
    val file = File("文件路徑")
    file
        .also {
            it.setExecutable(true)
            it.setReadable(true)
        }
        .also {
            println(it.readLines())
        }
}

takeIf 內(nèi)置函數(shù)

如果所傳遞函數(shù)對(duì)象中的條件滿足,就返回自身
一般情況下救巷,takeIf + 空合并操作符 一起使用

fun main() {
    val result = checkPermission("root", "12345")
    println(result)
}

fun checkPermission(username: String, userpwd: String): String {
    // 一般情況下壶熏,takeIf + 空合并操作符 一起使用
    return username.takeIf { checkInternal(username, userpwd) } ?: "普通用戶"
}

private fun checkInternal(username: String, userpwd: String): Boolean {
    return username == "root" && userpwd == "12345"
}

takeUnless 內(nèi)置函數(shù)

如果所傳遞函數(shù)對(duì)象中的條件不滿足,就返回自身
與 takeIf 功能相反
takeUnless+對(duì)象中的屬性判空一般一起使用浦译,可以驗(yàn)證對(duì)象的屬性有沒(méi)有設(shè)置值

class Manager {
    private var info: String? = null

    fun getInfo() = info

    fun setInfo(info: String) {
        this.info = info
    }
}

fun main() {
    val manager = Manager()

    // takeUnless+對(duì)象中的屬性判空一般一起使用棒假,可以驗(yàn)證對(duì)象的屬性有沒(méi)有設(shè)置值
    val r = manager.getInfo().takeUnless { it.isNullOrBlank() } ?: "未初始化"
    println(r)
}

四、集合與對(duì)象


List

盡量使用 getOrElse 或 getOrNull 獲取數(shù)組中元素

fun main() {
    val list = listOf("A", "B", "C")

    println(list[0])
    println(list.getOrElse(4) { "越界" })
    println(list.getOrNull(4) ?: "越界")
}

可變 List

fun main() {
    val list = mutableListOf("A", "B", "C")
    list.add("D")
    list.remove("D")
    
    val list2 = listOf(1, 2, 3)
    // list2.add  // 沒(méi)有這個(gè)函數(shù)
    // 不可變 List 轉(zhuǎn) 可變 List
    val list3 = list2.toMutableList()
    
    // 可變 List 轉(zhuǎn) 不可變 List
    val list4 = list.toList()
}

mutator函數(shù)管怠、removeIf

+= -=淆衷,靠運(yùn)算符重載實(shí)現(xiàn)
removeIf 有條件的移除List中元素,會(huì)自動(dòng)遍歷元素

fun main() {
    val list = mutableListOf("Zhangsan", "Lisi", "Zhangsi", "Wangwu")
    // mutator: += -=
    list += "趙六"
    list -= "Zhangsi"
    println(list)

    // removeIf
    list.removeIf { it.contains("Zhang") }
    println(list)
}

List 遍歷

fun main() {
    val list = listOf(1, 2, 3, 4, 5)

    // 第一種
    for (item in list) {
        println(item)
    }

    // 第二種
    list.forEach {
        println(it)
    }

    // 第三種渤弛,帶位置索引
    list.forEachIndexed { index, item ->
        println("index: $index, item: $item")
    }
}

解構(gòu)語(yǔ)法過(guò)濾元素

fun main() {
    val list = listOf("Zhangsan", "Lisi", "Wangwu")

    // 解構(gòu),元素值不可變
    val(value1, value2, value3) = list
    println("value1: $value1, value2: $value2, value3: $value3")

    // 解構(gòu)甚带,元素值可變
    var(v1, v2, v3) = list
    v1 = "ok"
    println("v1: $v1, v2: $v2, v1: $v3")

    // 解構(gòu)時(shí)過(guò)濾元素她肯,可以節(jié)省一點(diǎn)性能
    val(_, n2, n3) = list
    println("n2: $n2, n3: $n3")  // 沒(méi)有第一個(gè)
}

Set

不會(huì)出現(xiàn)重復(fù)元素
盡量使用 elementAtOrElse 或 elementAtOrNull 獲取元素

fun main() {
    val set = setOf("A", "B", "C", "A")

    println(set.elementAt(0))
    // println(set.elementAt(3)) // 崩潰
    println(set.elementAtOrElse(0) { "越界" })
    println(set.elementAtOrElse(3) { "越界" })
    println(set.elementAtOrNull(1) ?: "越界")
    println(set.elementAtOrNull(3) ?: "越界")
}

可變 Set

fun main() {
    val set = mutableSetOf("A", "B", "C", "A")
    set += "D"
    set -= "D"
    set.add("D")
    set.remove("D")
    set.removeIf { 
        it == "D"
    }
}

集合轉(zhuǎn)換與快捷函數(shù)

fun main() {
    val list = listOf("A", "A", "B", "C")

    // List 轉(zhuǎn) Set 去重
    val set = list.toSet()
    println(set)

    // List 轉(zhuǎn) Set 轉(zhuǎn) List 去重
    val list2 = list.toSet().toList()
    println(list2)

    // 快捷函數(shù) distinct 去重
    val list3 = list.distinct()  // 內(nèi)部:轉(zhuǎn) 可變Set 轉(zhuǎn) List
    println(list3)
}

數(shù)組

盡量使用elementAtOrElse 或 elementAtOrNull 獲取元素

        Kotlin語(yǔ)言中的各種數(shù)組類型,雖然是引用類型鹰贵,背后可以編譯成Java基本數(shù)據(jù)類型
        IntArray        intArrayOf
        DoubleArray     doubleArrayOf
        LongArray       longArrayOf
        ShortArray      shortArrayOf
        ByteArray       byteArrayOf
        FloatArray      floatArrayOf
        BooleanArray    booleanArrayOf
        Array           arrayOf             對(duì)象數(shù)組
fun main() {
    val intArray = intArrayOf(1, 2, 3)

    // 取元素
    intArray.elementAt(0)
    // intArray.elementAt(3)  // 崩潰

    intArray.elementAtOrElse(3) { -1 }
    intArray.elementAtOrNull(3) ?: "越界"

    // List 轉(zhuǎn) 數(shù)組
    val charArray = listOf('A', 'B').toCharArray()

    // 對(duì)象數(shù)組
    val objArray = arrayOf(File("1"), File("2"))
}

Map

fun main() {
    val map1 = mapOf("A" to(1.0), "B" to 2.0)
    val map2 = mapOf(Pair("A", 1.0), Pair("B", 2.0))
}

Map 中值的獲取

盡量使用 getOrDefault 或 getOrElse

fun main() {
    val map = mapOf("A" to (1.0), "B" to 2.0)

    // 方式一 []晴氨,等價(jià)于 get,獲取不到返回 null
    println(map["A"])
    println(map.get("A"))
    println(map["C"])

    // 方式二 getOrDefault
    println(map.getOrDefault("A", -1.0))
    println(map.getOrDefault("C", -1.0))

    // 方式二 getOrDefault
    println(map.getOrElse("A") { "找不到" })
    println(map.getOrElse("C") { "找不到" })

    // 方式三 getValue碉输,獲取不到會(huì)崩潰
    println(map.getValue("A"))
    println(map.getValue("C"))
}

Map 的遍歷

fun main() {
    val map = mapOf("A" to (1.0), "B" to 2.0)

    // 方式一
    map.forEach {
        println("key: ${it.key}, value: ${it.value}")
    }

    // 第二種
    map.forEach { key, value ->
        println("key: $key, value: $value")
    }

    // 第三種
    map.forEach { (key, value) ->
        println("key: $key, value: $value")
    }

    // 第四種
    for (item in map) {
        println("key: ${item.key}, value: ${item.value}")
    }
}

可變 Map

fun main() {
    val map = mutableMapOf("A" to (1.0), "B" to 2.0)

    // 操作:+= -= [] put
    map += "C" to 3.0
    map -= "C"
    map["C"] = 3.0
    map.put("C", 3.0) // 等價(jià)于 []

    // getOrPut 沒(méi)有的話就添加進(jìn)去
    map.getOrPut("D") { 4.0 }
    println(map["D"])

    // getOrPut 有的話就取出來(lái)
    map.getOrPut("A") { 4.0 }

    // getOrDefault 沒(méi)有就取傳遞的默認(rèn)值
    val r = map.getOrDefault("F", 6.0)
    println(r)
}

定義類籽前、field關(guān)鍵詞

對(duì)于已經(jīng)賦初始值的屬性,field 代表當(dāng)前屬性的值

class KtBaseClass {
    var name = "zhangsan"
    // 自動(dòng)生成隱式代碼,寫不寫都有
        get() = field
        set(value) {
            field = value
        }

    var info = "is ok"
    // 重寫隱式代碼枝哄,擴(kuò)展功能
        get() = field.capitalize()
        set(value) {
            field = "**${value}**"
        }
}

類的計(jì)算屬性 與 防范競(jìng)態(tài)條件

計(jì)算屬性:使用get函數(shù)覆蓋field的寫法
防范競(jìng)態(tài)條件:當(dāng)被調(diào)用的屬性可能為null時(shí)肄梨,需要使用防空指針的寫法編寫代碼,這種寫法稱為防范競(jìng)態(tài)條件

class KtBaseClass {
    val number: Int
        // 計(jì)算屬性挠锥,get函數(shù)覆蓋了field众羡,在對(duì)應(yīng)的java代碼中,也不會(huì)生成對(duì)應(yīng)名稱的屬性
        get() = (1..1000).shuffled().first()

    val info: String? = null
    // 防范競(jìng)態(tài)條件蓖租,當(dāng)被調(diào)用的成員可能為null時(shí)粱侣,就必須使用防范競(jìng)態(tài)條件
    fun getShowInfo(): String {
        // 這種寫法就是防范競(jìng)態(tài)條件,這種寫法會(huì)大量使用
        return info?.let {
            if (it.isBlank()) {
                "info 是 空值"
            } else {
                "info 結(jié)果:$it"
            }
        } ?: "info 是 null"
    }
}

類的主構(gòu)造函數(shù)

// 主構(gòu)造函數(shù):隱式自動(dòng)生成蓖宦,不需要寫
class KtBaseClass() {

}

// 主構(gòu)造函數(shù):傳入的值命名要按照 _xxx 的方式齐婴,傳入的值不能直接使用,需要接收后才能使用
class KtBaseClass(_name: String, _sex: Char, _age: Int) {

    var name = _name
        get() = field  // get不允許private
        private set(value) {
            field = value
        }

    val sex = _sex
        get() = field
        // set(value) {}  // 聲明為val不可修改稠茂,沒(méi)有set

    var age = _age
        get() = if (field < 0) 0 else field
        // 或
//        get() {
//            return if (field < 0) {
//                0
//            } else {
//                field
//            }
//        }

    fun show() {
        // println(_name) // 不允許直接使用
    }
}

fun main() {
    val p = KtBaseClass("zhangsan", 'm', -1)
    println(p.age)
}

在類的主構(gòu)造函數(shù)中定義屬性

// 在主構(gòu)造函數(shù)中直接定義屬性
class KtBaseClass(var name: String, val sex: Char, var age: Int) {

    fun show() {
        println(name)
    }
}

類的次構(gòu)造函數(shù)

class KtBaseClass(var name: String) {  // 主構(gòu)造

    constructor(name: String, sex: Char) : this(name) {  // 次構(gòu)造函數(shù)必須調(diào)用主構(gòu)造函數(shù)
        println("次構(gòu)造函數(shù):name: $name, sex: $sex")
    }
}


fun main() {
    val p = KtBaseClass("zhangsan", 'm')
    println(p.name)
}

構(gòu)造函數(shù)中參數(shù)的默認(rèn)值

class KtBaseClass(var name: String = "zhangsan") {  // 主構(gòu)造

    constructor(name: String = "zhangsan", sex: Char = 'm') : this(name) {  // 次構(gòu)造函數(shù)必須調(diào)用主構(gòu)造函數(shù)
        println("次構(gòu)造函數(shù):name: $name, sex: $sex")
    }
}


fun main() {
    val p = KtBaseClass("zhangsan", 'm')  // 調(diào)用次構(gòu)造函數(shù)
    println(p.name)

    val p2 = KtBaseClass()  // 優(yōu)先調(diào)用主構(gòu)造函數(shù)
}

類的初始化塊

class KtBaseClass(_name: String) {  // 主構(gòu)造

    // 相當(dāng)于Java的 {} 構(gòu)造代碼塊柠偶,在主構(gòu)造函數(shù)執(zhí)行時(shí)執(zhí)行
    // 可以使用構(gòu)造函數(shù)傳入的變量
    init {
        println("主構(gòu)造函數(shù)調(diào)用:name: $_name")

        // 可以在這里判斷傳入值的合法性,驗(yàn)證不通過(guò)會(huì)拋出異常
        require(_name.isNotBlank()) {
            "name不能是空值"
        }
    }

    constructor(name: String, sex: Char) : this(name) {  // 次構(gòu)造函數(shù)必須調(diào)用主構(gòu)造函數(shù)
        println("次構(gòu)造函數(shù):name: $name, sex: $sex")
    }
}

fun main() {
    KtBaseClass("zhangsan", 'm')  // 調(diào)用次構(gòu)造函數(shù)
}

構(gòu)造初始化順序

// 第一步:生成 val sex
class KtBaseClass(_name: String, val sex: Char) {  // 主構(gòu)造

    // 第二步:生成 val name主慰。與init代碼塊平級(jí)嚣州,寫在init代碼塊前所以先執(zhí)行
    val name = _name

    init {
        val nameValue = _name  // 第三步:生成 val nameValue
        println("init代碼塊調(diào)用:name: $_name")
    }


    constructor(name: String, sex: Char, age: Int) : this(name, sex) {  // 次構(gòu)造函數(shù)必須調(diào)用主構(gòu)造函數(shù)
        // 第五步:執(zhí)行次構(gòu)造函數(shù)
        println("次構(gòu)造函數(shù):name: $name, sex: $sex, age: $age")
    }

    // 第四步:生成 val info
    val info = "test info"
}

fun main() {
    KtBaseClass("zhangsan", 'm')  // 調(diào)用次構(gòu)造函數(shù)
}

延遲初始化 lazyinit

使用 lazyinit 的屬性前需要手動(dòng)初始化

class KtClass {
    lateinit var info: String

    fun loadInfo() {
        info = "初始化成功"
    }

    fun showInfo() {
        // if (info == null) {} // 不能這樣用,在info初始化前任何使用都會(huì)崩潰
        if (::info.isInitialized) {
            println("info: $info")
        } else {
            println("info還沒(méi)初始化")
        }
    }
}

fun main() {
    val p = KtClass()
    // println(p.info)  // lazyinit的屬性在初始化前使用會(huì)導(dǎo)致崩潰
    
//    p.loadInfo()
//    println(p.info)

    p.showInfo()
}

惰性初始化 by lazy

屬性在使用時(shí)自動(dòng)初始化

class KtClass {
    // 普通方式(餓漢式)
    val info1 = readFromDB1()

    val info2 by lazy { readFromDB2() }

    fun readFromDB1(): String {
        println("1開始讀取數(shù)據(jù)")
        println("1讀取數(shù)據(jù)中...")
        println("1讀取數(shù)據(jù)中...")
        println("1讀取數(shù)據(jù)中...")
        println("1讀取數(shù)據(jù)中...")
        println("1結(jié)束讀取數(shù)據(jù)")
        return "info1 load success"
    }

    fun readFromDB2(): String {
        println("2開始讀取數(shù)據(jù)")
        println("2讀取數(shù)據(jù)中...")
        println("2讀取數(shù)據(jù)中...")
        println("2讀取數(shù)據(jù)中...")
        println("2讀取數(shù)據(jù)中...")
        println("2結(jié)束讀取數(shù)據(jù)")
        return "info2 load success"
    }
}

fun main() {
    val p = KtClass()

    Thread.sleep(5000L)

    println("即將開始使用info1")
    println("讀取到的數(shù)據(jù):info: ${p.info1}")
    println()
    println("即將開始使用info2")
    println("讀取到的數(shù)據(jù):info: ${p.info2}")
}

// 輸出
1開始讀取數(shù)據(jù)
1讀取數(shù)據(jù)中...
1讀取數(shù)據(jù)中...
1讀取數(shù)據(jù)中...
1讀取數(shù)據(jù)中...
1結(jié)束讀取數(shù)據(jù)
即將開始使用info1
讀取到的數(shù)據(jù):info: info1 load success

即將開始使用info2
2開始讀取數(shù)據(jù)
2讀取數(shù)據(jù)中...
2讀取數(shù)據(jù)中...
2讀取數(shù)據(jù)中...
2讀取數(shù)據(jù)中...
2結(jié)束讀取數(shù)據(jù)
讀取到的數(shù)據(jù):info: info2 load success

初始化陷阱

注意屬性與init代碼塊定義代碼順序

class KtClass2 {
    
    init {
        // 與 var name 生成處于同一優(yōu)先級(jí)共螺,執(zhí)行順序與定義順序有關(guān)该肴,所以不能這樣用,必須先定義 val number
        number = number.times(9)
    }

    var number = 9
}

初始化陷阱2

class KtClass3 {

    var info: String

    init {
        getInfoMethod()  // info 還沒(méi)初始化就調(diào)用了
        info = "test info"  // 需要把這行放在上一行上面 
    }

    fun getInfoMethod() {
        println("info: ${info[0]}")
    }
}

fun main() {
    KtClass3()  // 會(huì)崩潰
}

初始化陷阱3

class KtClass5(_info: String) {

    var content: String = getInfoContent()

    var info = _info  // 需要把這行放到最前面

    fun getInfoContent() = info
}

fun main() {
    KtClass5("test").content.length  // 會(huì)崩潰
}

五藐不、Kotlin語(yǔ)言特點(diǎn)


類的繼承與重載的 open 關(guān)鍵詞

// Kotlin所有的類匀哄,默認(rèn)是final修飾的,不能被繼承雏蛮,與Java相反
// open:移除final修飾
open class Person(private val name: String) {

    private fun showName() = "父類的姓名是$name"

    // Kotlin所有的函數(shù)涎嚼,默認(rèn)是final修飾的,不能被重寫挑秉,與Java相反
    open fun  myPrint() = println(showName())
}

class Studend(private val subName: String): Person(subName) {

    private fun showName() = "子類的姓名是$subName"

    override fun myPrint() = println(showName())
}

fun main() {
    val person: Person = Studend("zhangsan")
    person.myPrint()
}

類型轉(zhuǎn)換

is
as

open class Person(private val name: String) {

    fun showName() = "父類的姓名是$name"

    // Kotlin所有的函數(shù)法梯,默認(rèn)是final修飾的,不能被重寫犀概,與Java相反
    open fun  myPrint() = println(showName())
}

class Student(private val subName: String): Person(subName) {

    private fun showName2() = "子類的姓名是$subName"

    override fun myPrint() = println(showName2())
}

fun main() {
    val person: Person = Student("zhangsan")
    person.myPrint()

    println(person is Student)  // true
    println(person is Person) // true

    // is + as
    if (person is Student) {
        (person as Student).myPrint()
    }

    if (person is Person) {
        println((person as Person).showName())
    }
}

智能類型轉(zhuǎn)換

在語(yǔ)句中使用 as 對(duì)對(duì)象obj進(jìn)行類型轉(zhuǎn)換后立哑,后就面的語(yǔ)句會(huì)自動(dòng)判斷obj的類型為轉(zhuǎn)換后的類型

open class Person(private val name: String) {

    fun showName() = "父類的姓名是$name"

    // Kotlin所有的函數(shù),默認(rèn)是final修飾的姻灶,不能被重寫铛绰,與Java相反
    open fun  myPrint() = println(showName())

    fun methodPerson() = println("父類的方法")
}

class Student(private val subName: String): Person(subName) {

    private fun showName2() = "子類的姓名是$subName"

    override fun myPrint() = println(showName2())

    fun methodStudent() = println("子類的方法")
}

fun main() {
    val person: Person = Student("zhangsan")

    // 智能類型轉(zhuǎn)換:這里調(diào)用了 as 轉(zhuǎn)換,下面的語(yǔ)句中的person會(huì)自動(dòng)判斷為Student類型
    (person as Student).methodStudent()
    person.methodStudent()  // 不需要再as進(jìn)行轉(zhuǎn)換
}

超類 Any

在Kotlin中所有的類都隱式繼承了 Any产喉,不需要寫
Any 類在Kotlin的設(shè)計(jì)中捂掰,只提供標(biāo)準(zhǔn)敢会,看不到實(shí)現(xiàn),實(shí)現(xiàn)在各個(gè)平臺(tái)處理好了
相當(dāng)于Java的Object这嚣,但是比Object實(shí)現(xiàn)的更高級(jí)

class Obj1: Any()

fun main() {
    println(Obj1().toString())
}

對(duì)象聲明

object 類名即是類名鸥昏,又是類的單例,只有一個(gè)創(chuàng)建疤苹,是典型的單例

// object KtObject 即是類的實(shí)例互广,又是類名
// 只有一個(gè)創(chuàng)建這是典型的單例
object KtObject {

    init {
        println("KtObject init")
    }

    fun show() = println("我是show函數(shù)")
}

fun main() {
    println(KtObject) // 三個(gè)打印一致
    println(KtObject)
    println(KtObject)

    KtObject.show()
}

對(duì)象表達(dá)式

匿名對(duì)象表達(dá)式:objct: 類名()
具名對(duì)象是常見的類聲明及對(duì)象初始化
對(duì)于Java接口,有兩種實(shí)現(xiàn)方式:object: 對(duì)象表達(dá)式 和 簡(jiǎn)潔版
對(duì)于Kotlin接口卧土,只有一種實(shí)現(xiàn)方式:object: 對(duì)象表達(dá)式

interface RunnableKt {
    fun run()
}

open class KtBaseClass {

    open fun add(info: String) = println("KtBaseClass add:$info")

    open fun del(info: String) = println("KtBaseClass del:$info")
}

fun main() {
    // 匿名對(duì)象 表達(dá)式
    val p = object: KtBaseClass() {
        override fun add(info: String) {
            println("匿名對(duì)象add: $info")
        }

        override fun del(info: String) {
            println("匿名對(duì)象del: $info")
        }
    }
    p.add("zhangsan")
    p.del("zhangsan")

    // 具名方式實(shí)現(xiàn)
    val p2 = KtBaseClassImpl()
    p2.add("zhangsan")
    p2.del("zhangsan")

    // 對(duì)Java的接口惫皱,用Kotlin對(duì)象表達(dá)式實(shí)現(xiàn)
    val p3 = object: Runnable {
        override fun run() {
            println("Runnable run ...")
        }
    }
    p3.run()

    // 對(duì)Java接口,用Java最簡(jiǎn)介方式實(shí)現(xiàn)
    val p4 = Runnable {
        fun run() {
            println("Runnable run2 ...")
        }
    }
    p4.run()

    // 對(duì)Kotlin的接口尤莺,用Kotlin對(duì)象表達(dá)式實(shí)現(xiàn)
    object: RunnableKt {
        override fun run() {
            println("RunnableKt run ...")
        }
    }.run()

    // 對(duì)Kotlin接口旅敷,用Java最簡(jiǎn)介方式實(shí)現(xiàn),不能這樣用
//    RunnableKt {
//
//    }
}

class KtBaseClassImpl: KtBaseClass() {
    override fun add(info: String) {
        println("具名對(duì)象add: $info")
    }

    override fun del(info: String) {
        println("具名對(duì)象del: $info")
    }
}

伴生對(duì)象

由來(lái):Kotlin中沒(méi)有Java的static颤霎,伴生對(duì)象類似于Java的static
無(wú)論類的對(duì)象構(gòu)建多少次媳谁,其中的伴生對(duì)象只初始化一次
無(wú)論伴生對(duì)象中的成員被調(diào)用多少次,伴生對(duì)象只初始化一次

class KtCompanion {

    // 伴生對(duì)象
    companion object {
        val info = "test info"
        fun show() = println("info: $info")
    }
}

fun main() {
    println(KtCompanion.Companion.info)

    KtCompanion.Companion.show()

    KtCompanion()
    KtCompanion()
}

內(nèi)部類

inner 修飾
內(nèi)部類 能訪問(wèn) 外部類(加inner修飾)友酱,外部類 能訪問(wèn) 內(nèi)部類
嵌套類 不能訪問(wèn) 外部類晴音,外部類 能訪問(wèn) 嵌套類

// 內(nèi)部類
class Body(_info: String) {

    val bodyInfo = _info

    fun show() {
        Heart().run()
    }

    inner class Heart {  // 默認(rèn)情況下:內(nèi)部的類 不能訪問(wèn) 外部的類,內(nèi)部的類要加修飾符 inner 才能訪問(wèn)外部的類
        fun run() = "心臟訪問(wèn)身體信息:$bodyInfo"
    }

    inner class Hand {
        inner class LeftHand {
            fun run() = "左手訪問(wèn)身體信息:$bodyInfo"
        }

        inner class RightHand {
            fun run() = "右手訪問(wèn)身體信息:$bodyInfo"
        }
    }
}

// 嵌套類
class Outer {

    val info = "ok"

    fun show() {
        Nested().output()  // 外部類 能訪問(wèn) 嵌套類
    }

    class Nested {

//        fun output() = println("嵌套類: $info")  // 嵌套類 不能訪問(wèn) 外部類
        fun output() = println("嵌套類")
    }
}

fun main() {
    // 內(nèi)部類
    Body("ok").Heart().run()

    // 嵌套類
    Outer.Nested().output()
}

數(shù)據(jù)類

data 修飾符

// 普通類
class ResponseResultBean1(var code: Int, var msg: String, var data: String)

// 數(shù)據(jù)類缔杉,一般用于 Bean 類锤躁,默認(rèn)提供了 get set 構(gòu)造函數(shù) 解構(gòu)操作 copy hashCode toString equals
data class ResponseResultBean2(var code: Int, var msg: String, var data: String)

fun main() {
    println(ResponseResultBean1(200, "ok", "data"))

    println(ResponseResultBean2(200, "ok", "data"))  // 打印屬性的值

    println(
        ResponseResultBean1(200, "ok", "data") == ResponseResultBean1(200, "ok", "data")
    )  // false

    println(
        ResponseResultBean2(200, "ok", "data") == ResponseResultBean2(200, "ok", "data")
    )  // true
}

數(shù)據(jù)類默認(rèn)的 copy equals hashCode toString 函數(shù)

數(shù)據(jù)類默認(rèn)的 copy equals hashCode toString 函數(shù)只管主構(gòu)造函數(shù)中的屬性,不管次構(gòu)造函數(shù)中的屬性或详,使用以上函數(shù)時(shí)需要注意

data class DataClass(var name: String , var age: Int) {

    var coreInfo = ""

    constructor(name: String): this(name, 20) {
        coreInfo = "核心信息"
    }

    // 需要重寫才有次構(gòu)造函數(shù)中初始化的屬性值
    override fun toString(): String {
        return "toString name: $name, age: $age, coreInfo: $coreInfo"
    }
}

fun main() {
    val p1 = DataClass("zhangsan")
    println(p1)

    val p2 = p1.copy("lisi", 23)
    println(p2)
}

解構(gòu)聲明

解構(gòu)必須是按照順序聲明的系羞,且從 component1 開始

class Student(var name: String, var age: Int, var sex: Char) {

    operator fun component1() = name
    operator fun component2() = age
    operator fun component3() = sex
}

fun main() {
    val(name, age, sex) = Student("zhangsan", 20, 'm')
    println("name: $name, age: $age, sex: $sex")

    val(name2, age2, _) = Student("zhangsan", 20, 'm')
    println("name: $name2, age: $age2")
}

運(yùn)算符重載

class AddClass(var number1: Int , var number2: Int) {
    operator fun plus(p: AddClass): Int {
        return number1 + p.number1 + number2 + p.number2
    }
}

fun main() {
    val p1 = AddClass(1, 2)
    val p2 = AddClass(3, 4)
    println(p1 + p2)
}

枚舉

enum class
枚舉的值等價(jià)于枚舉本身

enum class Week {
    星期一,
    星期二,
    星期三,
    星期四,
    星期五,
    星期六,
    星期日;
}

fun main() {
    println(Week.星期一)

    // 枚舉的值等價(jià)于枚舉本身
    println(Week.星期一 is Week)  // true
}

枚舉類定義函數(shù)

data class LimbsInfo(var name: String, var length: Int) {
    fun show() {
        println("${name}的長(zhǎng)度是$length")
    }
}

enum class Limbs(private val limbsInfo: LimbsInfo) {
    LEFT_HAND(LimbsInfo("左手", 88)),
    RIGHT_HAND(LimbsInfo("右手", 88)),

    LEFT_FOOT(LimbsInfo("左腳", 100)),
    RIGHT_FOOT(LimbsInfo("右腳", 100));

    fun show() = "四肢信息:${limbsInfo.name},長(zhǎng)度:${limbsInfo.length}"

    // 更新
    fun update(limbsInfo: LimbsInfo) {
        this.limbsInfo.name = limbsInfo.name
        this.limbsInfo.length = limbsInfo.length
    }
}

fun main() {
    println(Limbs.LEFT_HAND.show())

    // 更新
    Limbs.LEFT_HAND.update(LimbsInfo("左手", 89))
}

代數(shù)數(shù)據(jù)類型

when 判斷枚舉值

enum class Exams {
    Fraction1,
    Fraction2,
    Fraction3,
    Fraction4
}

class Teacher(private val exam: Exams) {
    fun show() =
        when (exam) {
            Exams.Fraction1 -> "不及格"
            Exams.Fraction2 -> "及格"
            Exams.Fraction3 -> "優(yōu)良"
            Exams.Fraction4 -> "優(yōu)秀"
            // else -> 上面已經(jīng)包含全部值霸琴,可省略
        }
}

fun main() {
    println(Teacher(Exams.Fraction4).show())
}

密封類

scaled
成員必須有類型椒振,并且繼承本類

sealed class Exams {
     object Fraction1: Exams()
     object Fraction2: Exams()
     object Fraction3: Exams()
     class Fraction4(val name: String): Exams();
}

class Teacher(private val exam: Exams) {
    fun show() =
        when (exam) {
            is Exams.Fraction1 -> "不及格"
            is Exams.Fraction2 -> "及格"
            is Exams.Fraction3 -> "優(yōu)良"
            is Exams.Fraction4 -> "優(yōu)秀,學(xué)生姓名是: ${(this.exam as Exams.Fraction4).name}"
        }
}

fun main() {
    println(Teacher(Exams.Fraction3).show())
    println(Teacher(Exams.Fraction4("zhangsan")).show())
}

數(shù)據(jù)類使用條件

1. 服務(wù)器返回的響應(yīng)數(shù)據(jù) Java Bean 基本可以使用數(shù)據(jù)類
2. 數(shù)據(jù)類必須有至少一個(gè)參數(shù)的主構(gòu)造函數(shù)
3. 數(shù)據(jù)類必須有參數(shù)
4. 數(shù)據(jù)類不能使用 open abstract inner scaled 等修飾
5. 需要 比較梧乘、copy澎迎、toString、解構(gòu) 等豐富功能時(shí)选调,可使用數(shù)據(jù)類


六火诸、

接口的定義

interface

interface IUSB {
    var versionInfo: String
    var deviceInsertInfo: String

    fun insert(): String
}

class Mouse(override var versionInfo: String = "USB 3.0", override var deviceInsertInfo: String = "鼠標(biāo)插入U(xiǎn)SB接口") : IUSB {
    override fun insert(): String = "Mouse $versionInfo: $deviceInsertInfo"
}

class Keyboard: IUSB {
    override var versionInfo: String = "USB 3.1"
        get() = field
        set(value) {
            field = value
        }

    override var deviceInsertInfo: String = "鍵盤插入U(xiǎn)SB接口"
        get() {
            println("你get了$field")
            return field
        }
        set(value) {
            field = value
            println("你set了$value")
        }

    override fun insert(): String = "Keyboard $versionInfo: $deviceInsertInfo"
}

接口的默認(rèn)實(shí)現(xiàn)

雖然接口成員可以通過(guò) get set 給其賦默認(rèn)值十偶,但是不建議這樣做,因?yàn)榻涌诒緛?lái)就是用來(lái)定義標(biāo)準(zhǔn)的

interface IUSB {
    // 接口成員不論是val還是var茸苇,都不能給其賦值(但有其它方法:get)
    // 任何類各吨、接口的val成員是只讀的枝笨,不能動(dòng)態(tài)改變其值(但是有其它方法:set)
    val versionInfo: String
        get() = (1..100).shuffled().first().toString()
        // set(value) val 不能有set

    val deviceInsertInfo: String
        get() = "接入設(shè)備"
        // set(value) val 不能有set

    fun insert(): String
}

class Mouse() : IUSB {

    override val versionInfo: String
        get() = super.versionInfo

    override val deviceInsertInfo: String
        get() = super.deviceInsertInfo
    
    override fun insert(): String = "Mouse $versionInfo: $deviceInsertInfo"
}

抽象類

abstract class BaseActivity {

    fun onCreate() {
        setContentView(getLayoutId())
        initView()
        initData()
    }

    private fun setContentView(layoutId: Int) {
        println("加載布局:$layoutId")
    }

    abstract fun getLayoutId(): Int
    abstract fun initView()
    abstract fun initData()
}

class MainActivity: BaseActivity() {
    override fun getLayoutId(): Int = 123

    override fun initView() {
        println("init view")
    }

    override fun initData() {
        println("init data")
    }

    fun show() {
        super.onCreate()
    }
}

fun main() {
    MainActivity().show()
}

泛型類

class KtBaseTClass<T>(private val obj: T) {
    fun show() = println("萬(wàn)能輸出器:$obj")
}

data class Student2(val name: String, val age: Int, val sex: Char)

fun main() {
    val student = Student2("zhangsan", 22, 'm')
    KtBaseTClass(student).show()
}

泛型函數(shù)

與 let apply also run 等結(jié)合使用

class KtClassGetter<T>(private val isOk: Boolean, private val obj: T) {
    fun getClass() {
        obj.takeIf { isOk }
    }
}

泛型類型轉(zhuǎn)換

fun <I, O> map(inputType: I, isMap: Boolean = true, mapAction: (I) -> O) =
    if (isMap) mapAction(inputType) else null

fun main() {
    val r = map(123) {
        it.toString()
    }
    println(r)
}

泛型類型約束

<T: xxx> 代表只接收 xxx 及其 子類

class KtClassTranslator<T: Student>(private val inputType: T, private val isR: Boolean = true) {
    fun getObj() = inputType.takeIf { isR }
}

vararg關(guān)鍵詞(動(dòng)態(tài)參數(shù))

class KtVarargClass<T>(vararg objects: T, var isMap: Boolean) {

    // out: T 只能被讀取
    private val objectArray: Array<out T> = objects

    fun showObj(index: Int) = objectArray[index].takeIf { isMap }

    fun <O> mapObj(index: Int, mapAction: (T?) -> O) = mapAction(objectArray[index].takeIf { isMap })
}

fun main() {
    // 由于傳遞的動(dòng)態(tài)參數(shù)中袁铐,包含多種類型,所以泛型真正的類型是 KtVarargClass<{Comparable<*> & java.io.Serializable}>
    // 但是不允許這樣寫横浑,所以使用 Any? 代替
    val p: KtVarargClass<Any?> = KtVarargClass("zhangsan", 1234, 5678.9, null, false, isMap = true)

    println(p.showObj(0))
    println(p.showObj(3))

    // map
    // mapAction中聲明T為可空的(?)剔桨,所以it也是可空的(?)
    val r = p.mapObj(1) {
        "轉(zhuǎn)化為String: $it"
    }
    println(r)

    val p2 = KtVarargClass("zhangsan", "lisi", "wangwu", isMap = true)
    println(p2.showObj(0))

    // p2傳遞的動(dòng)態(tài)參數(shù)中,只包含String類型徙融,所以it的類型為String?
    val r2 = p2.mapObj(1) {
        it?.length
    }
    println(r2)
}


[ ]操作符

class KtGetOperator<T>(vararg val objects: T, var isR: Boolean = true) {

    // []運(yùn)算符重載
    operator fun get(index: Int): T? {
        return objects[index].takeIf { isR }
    }
}

fun main() {
    val p = KtGetOperator("zhangsan", "lisi", null)
    // 只要有一個(gè)元素為null洒缀,那么所有的元素都是 String?
    val r: String? = p.get(1)
    println(p.get(0))
    println(p.get(2))
}

out 協(xié)變

在父類泛型聲明處,可以接受子類

// 生產(chǎn)者 out T 協(xié)變欺冀,T只能被讀取树绩,不能修改
interface Producer<out T> {

    // 編譯錯(cuò)誤:T不能被修改
    // fun consumer(item: T)

    fun product(): T
}

// 消費(fèi)者 in T 逆變,T不能被讀取隐轩,只能修改
interface Consumer<in T> {

    // 編譯錯(cuò)誤:T不能被修改
    fun consumer(item: T)

    // 編譯錯(cuò)誤:T不能被讀取
    // fun product(): T
}

// 生產(chǎn)者與消費(fèi)者 T 默認(rèn)情況下是不變饺饭,T能被修改,也能讀取
interface ProducerAndConsumer<T> {

    fun consumer(item: T)

    fun product(): T
}

open class Animal
open class Human: Animal()
open class Man: Human()
open class Woman: Human()

class ProducerClass1: Producer<Animal> {
    override fun product(): Animal {
        println("生產(chǎn)者 Animal")
        return Animal()
    }
}

class ProducerClass2: Producer<Human> {
    override fun product(): Human {
        println("生產(chǎn)者 Human")
        return Human()
    }
}

class ProducerClass3: Producer<Man> {
    override fun product(): Man {
        println("生產(chǎn)者 Man")
        return Man()
    }
}

class ProducerClass4: Producer<Woman> {
    override fun product(): Woman {
        println("生產(chǎn)者 Woman")
        return Woman()
    }
}

fun main() {
    val p1: Producer<Animal> = ProducerClass1()
    // ProducerClass2聲明的泛型為Human职车,p2聲明為Animal不報(bào)錯(cuò)瘫俊,是因?yàn)榉盒吐暶鳛?out,如果非 out 會(huì)報(bào)錯(cuò)
    // out 相當(dāng)于java中的 ? extends T悴灵,
    // 例如:List<? extends CharSequence> list = new ArrayList<String>()
    val p2: Producer<Animal> = ProducerClass2()
    val p3: Producer<Animal> = ProducerClass3()
    val p4: Producer<Animal> = ProducerClass4()
}

in 和 out

使用場(chǎng)景:
in 逆變:只能修改扛芽,不能讀取
out 協(xié)變:只能讀取,不能修改

class SetClass<in T> {

    fun set1(item: T) {

    }
}

class GetClass<out T>(_item: T) {

    val item = _item
    
    fun get1(): T {
        return item
    }
}

refield 關(guān)鍵詞

class ObjectClass1(val name:String, val age: Int, val sex: Char)
class ObjectClass2(val name:String, val age: Int, val sex: Char)
class ObjectClass3(val name:String, val age: Int, val sex: Char)

class RandomClass {

    inline fun <reified T> randomOrDefault(defaultAction: () -> T): T {
        val objList = listOf(
            ObjectClass1("zhangsan", 22, 'm'),
            ObjectClass2("lisi", 23, 'm'),
            ObjectClass3("wangwu", 24, 'm'),
        )
        val randomObj = objList.shuffled().first()

        println("產(chǎn)生的隨機(jī)對(duì)象:$randomObj")

        // 如果不聲明 reified T积瞒,it as T 會(huì)報(bào)錯(cuò)
        // 注意 as T?川尖,當(dāng)takeIf條件不成立時(shí),null as T 會(huì)導(dǎo)致崩潰赡鲜,所以要 T?
        return randomObj.takeIf { it is T } as T? ?: defaultAction()
    }
}

fun main() {
    val result = RandomClass().randomOrDefault<ObjectClass1> {
        println("使用默認(rèn)值")
        ObjectClass1("zhangsan", 22, 'm')
    }

    println("結(jié)果:$result")
}

定義擴(kuò)展函數(shù)

類名.擴(kuò)展函數(shù)名()

class KtExtensionBaseClass(val name: String, val age: Int, val sex: Char)

// 擴(kuò)展函數(shù)空厌,會(huì)自動(dòng)生成this引用
fun KtExtensionBaseClass.show() = println("顯示信息:name:$name, age:$age, sex:$sex")

// 其它框架中的類也可以添加擴(kuò)展函數(shù)
fun String.addAction(count: Int) = this + "@".repeat(count)

fun main() {
    KtExtensionBaseClass("zhangsan", 22, 'm').show()
    println("lisi".addAction(3))
}

在超類上定義擴(kuò)展函數(shù)

1. 自己定義的擴(kuò)展函數(shù)不能重復(fù)
2. Kotlin內(nèi)置的擴(kuò)展函數(shù),我們可以重新定義進(jìn)行覆蓋

fun Any.showContent() = println("內(nèi)容是:$this")

fun Any.showContent2(): Any {
    println("內(nèi)容2是:$this")
    return this
}

fun File.readLine() {
    println("覆蓋系統(tǒng)的擴(kuò)展函數(shù)")
}

data class ResponseResult(val msg: String, val code: Int)

fun main() {
    ResponseResult("success", 200).showContent()

    "zhangsan".showContent2().showContent()

    File("").readLines()
}

泛型擴(kuò)展函數(shù)

fun <T> T.showContentInfo() = println("${if (this is String) "字符串長(zhǎng)度是:$length" else "不是字符串银酬,內(nèi)容是:$this"}")

fun commonFun() {}

fun main() {
    "test".showContentInfo()
    val p = 123
    p.showContentInfo()
    
    commonFun().showContentInfo()
}

標(biāo)準(zhǔn)函數(shù)與泛型擴(kuò)展函數(shù)

private inline fun <I, O> I.mLet(lambda: (I) -> O) = lambda(this)

fun main() {
    val r = "test1".mLet {
        it.length
    }
    println(r)
}

val 類名.屬性名
get() = "xxx"

擴(kuò)展屬性

val String.myInfo
    get() = "zhangsan"

fun main() {
    println("".myInfo)
}

可空類型的擴(kuò)展函數(shù)

類名?.擴(kuò)展函數(shù)名()

fun String?.outputValue(defaultValue: String) = println(this ?: defaultValue)

fun main() {
    val nullValue: String? = null
    nullValue.outputValue("默認(rèn)值1")

    val value = "zhangsan"
    value.outputValue("默認(rèn)值2")
}

infix 關(guān)鍵詞

infix:中綴表達(dá)式嘲更,可以簡(jiǎn)化代碼
1. 需要與擴(kuò)展函數(shù)一起使用
2. 需要在函數(shù)的參數(shù)中傳遞一個(gè)參數(shù)

private infix fun <C1, C2> C1.gogo(c2: C2) {
    println("中綴表達(dá)式:第一個(gè)參數(shù):$this")
    println("中綴表達(dá)式:第二個(gè)參數(shù):$c2")
}

fun main() {
    // Kotlin自帶,
    // 例如:public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
    mapOf("零".to(0))
    mapOf("一" to 1)
    mapOf("二" to 2)

    // 自定義
    "張三".gogo("李四")
    "wangwu" gogo 1
}

定義擴(kuò)展文件

在實(shí)際中比較有用揩瞪,可以把很多擴(kuò)展操作定義在一個(gè)文件中赋朦,使用的時(shí)候引入,方便管理

KtExtension.kt 中定義

package com.demo.lib.ext

fun <E> Iterable<E>.randomItemValue() = this.shuffled().first()

MyClass.kt 中使用

import com.demo.lib.ext.randomItemValue

fun main() {
    val p = listOf("zhangsan", "lisi", "wangwu").randomItemValue()
    println(p)
}

重命名擴(kuò)展

import xxx.xxx.xxx as xxx

import com.demo.lib.ext.randomItemValue as f

fun main() {
    val p = listOf("zhangsan", "lisi", "wangwu").f()
    println(p)
}

apply 函數(shù)詳解

// INPUT.myApply李破,泛型擴(kuò)展函數(shù): 讓所有的類型宠哄,都可以 xxx.myApply 調(diào)用
// INPUT.():讓匿名函數(shù)持有 this
private inline fun <INPUT> INPUT.myApply(lambda: INPUT.() -> Unit): INPUT {
    lambda()
    return this
}

fun main() {
    File("xxx")
        .myApply {
            setReadable(true)
        }
        .myApply {
            setWritable(true)
        }
}

DSL (Domain Specified Language),領(lǐng)域?qū)S谜Z(yǔ)言

// apply5函數(shù)嗤攻,就是DSL編程范式毛嫉,定義輸入輸出等規(guī)則。
// 1.定義lambda規(guī)則標(biāo)準(zhǔn)妇菱,輸入 必須是Context類承粤,才能調(diào)用apply5函數(shù)
// 2.定義lambda規(guī)則標(biāo)準(zhǔn)暴区,輸出 始終返回Context本身
inline fun Context.apply5(lambda: Context.(String) -> Unit): Context {
    lambda(info)
    return this
}

inline fun File.applyFile(action: (String, String?) -> Unit): File {
    action(name, readLines()[0])
    return this
}

fun main() {
    val context = Context().apply5 {
        // 同時(shí)持有this和it
        toast("success")
        // it == String
        toast(it)
        toast(name)
    }
    println(context)

    File("xxx")
        .applyFile { fileName, content ->
            println("文件名:$fileName, 內(nèi)容:$content")
        }
        .applyFile { fileName, content ->
            
        }
}

變換函數(shù)-map

map:生成一個(gè)新的集合,新集合中每個(gè)元素根據(jù)lambda表達(dá)式進(jìn)行轉(zhuǎn)換

fun main() {
    val list = listOf("zhansan", "lisi", "wangwu")
    list
        .map {
            "姓名:$it"
        }
        .map {
            println("$it")
        }
}

變換函數(shù)-flatMap

flatMap:生成一個(gè)新的集合辛臊,新集合中每個(gè)元素都是集合仙粱,新集合中每個(gè)元素根據(jù)lambda表達(dá)式進(jìn)行轉(zhuǎn)換生成

fun main() {
    val list = listOf("zhansan", "lisi", "wangwu")
    // flatMap返回的是 List<List<String>>,但最終會(huì)進(jìn)行平鋪返回 List<String>
    val newList: List<String> = list.flatMap {
        listOf("$it 在學(xué)習(xí)Java", "$it 在學(xué)習(xí)Kotlin")
    }
    println(newList)
}
// [zhansan 在學(xué)習(xí)Java, zhansan 在學(xué)習(xí)Kotlin, lisi 在學(xué)習(xí)Java, lisi 在學(xué)習(xí)Kotlin, wangwu 在學(xué)習(xí)Java, wangwu 在學(xué)習(xí)Kotlin]

過(guò)濾函數(shù)-filter

filter:生成一個(gè)新的集合彻舰,新集合中每個(gè)元素符合給定的規(guī)則

fun main() {
    val list = listOf(
        listOf("zhangsan", "lisi"),
        listOf("wangwu", "zhaoliu")
    )

    list.map {
        it -> it.filter {
            it.contains('z')
        }
    }.map {
        println(it)
        // [zhangsan]
        // [zhaoliu]
    }

    list.flatMap {
        it -> it.filter {
            it.contains('z')
        }
    }.map {
        println(it)
        // zhangsan
        // zhaoliu
    }
}

合并函數(shù)-zip

zip:根據(jù)兩個(gè)集合中的元素生成一個(gè)新的集合伐割,與原集合index一致,并且新集合元素?cái)?shù)量為原兩個(gè)集合數(shù)量最小值

fun main() {
    val nameList = listOf("zhangsan", "lisi", "wangwu")
    val ageList = listOf(22, 23)
    val newList = nameList.zip(ageList)
    println(newList)
    // [(zhangsan, 22), (lisi, 23)]

    newList.forEach {
        println("nane: ${it.first}, age: ${it.second}")
    }
    // nane: zhangsan, age: 22
    // nane: lisi, age: 23
}

函數(shù)式編程

簡(jiǎn)化代碼刃唤,例如 zip 函數(shù)的 Java 實(shí)現(xiàn)遠(yuǎn)比 Kotlin 實(shí)現(xiàn)所用代碼多很多

Kotlin與Java互操作性和可空性

Kotlin調(diào)用Java代碼時(shí)隔心,Java給Kotlin的值,都是類似于 String!透揣,
所以kotlin在接受Java給的值時(shí)济炎,直接把類型聲明為可空?,避免代碼出錯(cuò)

fun main() {
    // 錯(cuò)誤
    println(JavaClass().info1.length)
    println(JavaClass().info2.length)  // 崩潰

    // 錯(cuò)誤
    val info1 = JavaClass().info1
    val info2 = JavaClass().info2
    println(info1.length)
    println(info2.length)  // 崩潰

    // 正確
    // Kotlin調(diào)用Java代碼時(shí)辐真,Java給Kotlin的值须尚,都是類似于 String!
    val info11 = JavaClass().info1
    val info21 = JavaClass().info2
    println(info11?.length)
    println(info21?.length)

    // 正確,推薦
    // Kotlin調(diào)用Java代碼時(shí)侍咱,Java給Kotlin的值耐床,都是類似于 String!
    // 所以kotlin在接受Java給的值時(shí),直接把類型聲明為可空?楔脯,避免代碼出錯(cuò)
    val info12: String? = JavaClass().info1
    val info22: String? = JavaClass().info2
    println(info12?.length)
    println(info22?.length)
}

單例模式

1. 餓漢式

object SigletonDemo

2. 懶漢式

class SingletonDemo private constructor() {

    companion object {
        private var instance: SingletonDemo? = null
            get() {
                if (field == null) {
                    field = SingletonDemo()
                }
                return field
            }

        fun getInstanceAction(): SingletonDemo = instance !!
    }

    fun show() = println("show")
}

fun main() {
    SingletonDemo.getInstanceAction().show()
}

3. 懶漢式+線程安全:@Synchronized

class SingletonDemo private constructor() {

    companion object {
        private var instance: SingletonDemo? = null
            get() {
                if (field == null) {
                    field = SingletonDemo()
                }
                return field
            }

        @Synchronized
        fun getInstanceAction(): SingletonDemo = instance !!
    }

    fun show() = println("show")
}

fun main() {
    SingletonDemo.getInstanceAction().show()
}

4. 懶漢式+雙重校驗(yàn)安全:by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { ... }

class SingletonDemo private constructor() {

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

    fun show() = println("show")
}

fun main() {
    SingletonDemo.instance.show()
}

注解 @JvmName 與 Kotlin

@JvmName 用于Kotlin文件撩轰,在Kotlin文件自動(dòng)生成Java類時(shí),指定類名稱
@file:JvmName("指定的類名")
KtBase.kt

@file:JvmName("Stu")

package com.demo.lib

fun showStudentInfo(info: String) = println(info)

JavaCallKotlin.java

public class JavaCallKotlin {
    public static void main(String[] args) {
//        KtBaseKt.showStudentInfo("info");
        Stu.showStudentInfo("info");
    }
}

注解 @JvmField 與 Kotlin

在Kotlin類的屬性上增加@JvmField昧廷,會(huì)剔除自動(dòng)生成的getXxx()方法,在Java調(diào)用該屬性時(shí)可直接調(diào)用
Persons.kt

class Persons {
    @JvmField
    val names = listOf("zhangsan", "lisi", "wangwu")
}

JavaCallKotlin.java

public class JavaCallKotlin {
    public static void main(String[] args) {
        Persons persons = new Persons();
        // 如果不加 @JvmField皆串,只能調(diào)用getXxx()方法獲取屬性
        for (String name : persons.getNames()) {
            System.out.println(name);
        }

        // 加了 @JvmField,可以直接調(diào)用屬性眉枕,不會(huì)再生成getXxx()方法
        for (String name : persons.names) {
            System.out.println(name);
        }
    }
}

注解 @JvmOverloads 與 Kotlin

@JvmOverloads 用于Kotlin函數(shù)谤牡,在Java調(diào)用Kotlin函數(shù)時(shí)姥宝,可以使用其定義好的參數(shù)默認(rèn)值
原理是編譯器會(huì)生成多個(gè)傳遞不同參數(shù)的重載方法腊满,給Java用
KtBase.kt

fun show(name: String, age: Int = 20, sex: Char = 'm') {
    println("name: $name, age: $age, sex: $sex")
}

@JvmOverloads
fun toast(name: String, sex: Char = 'm') {
    println("name: $name, sex: $sex")
}

JavaCallKotlin.java

public class JavaCallKotlin {
    public static void main(String[] args) {
        // KtBaseKt.show("zhangsan");  // Java不能享用Kotlin函數(shù)的參數(shù)默認(rèn)值
        KtBaseKt.toast("zhangsan"); // Kotlin函數(shù)加上@JvmOverloads后断序,Java可以享用參數(shù)默認(rèn)值
    }
}

注解 @JvmStatic 與 Kotlin

MyObject.kt

class MyObject {

    companion object {
        @JvmField  // 編譯時(shí)會(huì)把TARGET移出Companion內(nèi)部類
        val TARGET = "公園"

        @JvmStatic // 編譯時(shí)會(huì)把showAction方法移出Companion內(nèi)部類
        fun showAction(name: String) = println("${name}要去${TARGET}玩")
    }
}

JavaCallKotlin.java

public class JavaCallKotlin {
    public static void main(String[] args) {
        // System.out.println(MyObject.Companion.getTARGET()); // 不加@JvmField流纹,只能通過(guò).Companion調(diào)用
        MyObject.Companion.showAction("zhangsan");  // 不加@JvmStatic,只能通過(guò).Companion調(diào)用

        System.out.println(MyObject.TARGET);  // 加上@JvmField违诗,可以直接調(diào)用,并且會(huì)剔除getXxx()方法
        MyObject.showAction("zhangsan");  // 加上@JvmStatic疮蹦,可以直接調(diào)用
    }
}

手寫事件變換操作符-create

fun main() {
    create { 
        "zhangsan"
    }
}

class RxCoreClass() {

}

inline fun <OUTPUT> create(action: () -> OUTPUT): RxCoreClass {
    
}

手寫事件變換操作符-中轉(zhuǎn)站

fun main() {
    create {
        "zhangsan"
    }
}

class RxCoreClass<T>(valueItem: T) {
    // create 操作符诸迟,最后一行的返回值,流向此處
}

inline fun <OUTPUT> create(action: () -> OUTPUT) = RxCoreClass(action())

手寫事件變換操作符-map

fun main() {
    create {
        "zhangsan"
    }.map {

    }.map {
        
    }
}

// valueItem == create 操作符 最后一行的返回值愕乎,流向此處
class RxCoreClass<T>(var valueItem: T)

inline fun <I, O> RxCoreClass<I>.map(mapAction: I.() -> O): RxCoreClass<O> {
    return RxCoreClass(mapAction(valueItem))
}

inline fun <OUTPUT> create(action: () -> OUTPUT) = RxCoreClass(action())

手寫事件變換操作符-observer

fun main() {
    create {
        123
    }.map {
        "值是:$this"
    }.map { 
        "長(zhǎng)度是:$length"
    }.observer {
        println(this)
    }
}

// valueItem == create 操作符 最后一行的返回值阵苇,流向此處
class RxCoreClass<T>(var valueItem: T)

inline fun <I, O> RxCoreClass<I>.map(mapAction: I.() -> O): RxCoreClass<O> = RxCoreClass(mapAction(valueItem))

inline fun <I> RxCoreClass<I>.observer(observerAction: I.() -> Unit) = observerAction(valueItem)

inline fun <OUTPUT> create(action: () -> OUTPUT) = RxCoreClass(action())
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市感论,隨后出現(xiàn)的幾起案子绅项,更是在濱河造成了極大的恐慌,老刑警劉巖比肄,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件快耿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡芳绩,警方通過(guò)查閱死者的電腦和手機(jī)掀亥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)妥色,“玉大人搪花,你說(shuō)我怎么就攤上這事∴诤Γ” “怎么了撮竿?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)笔呀。 經(jīng)常有香客問(wèn)我幢踏,道長(zhǎng),這世上最難降的妖魔是什么凿可? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任惑折,我火速辦了婚禮,結(jié)果婚禮上枯跑,老公的妹妹穿的比我還像新娘惨驶。我一直安慰自己,他們只是感情好敛助,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布粗卜。 她就那樣靜靜地躺著,像睡著了一般纳击。 火紅的嫁衣襯著肌膚如雪续扔。 梳的紋絲不亂的頭發(fā)上攻臀,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音纱昧,去河邊找鬼刨啸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛识脆,可吹牛的內(nèi)容都是我干的设联。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼灼捂,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼离例!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起悉稠,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤宫蛆,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后的猛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體耀盗,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年衰絮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了袍冷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡猫牡,死狀恐怖胡诗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情淌友,我是刑警寧澤煌恢,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站震庭,受9級(jí)特大地震影響瑰抵,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜器联,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一二汛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拨拓,春花似錦肴颊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春竟宋,著一層夾襖步出監(jiān)牢的瞬間提完,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工丘侠, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留徒欣,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓婉陷,卻偏偏與公主長(zhǎng)得像帚称,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子秽澳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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

  • 五、Lambda編程 1.Lambda表達(dá)式和成員引用 Lambda簡(jiǎn)介:作為函數(shù)參數(shù)的代碼塊戏羽〉I瘢可以理解為簡(jiǎn)化表達(dá)...
    TomyZhang閱讀 444評(píng)論 0 0
  • 基本類型: 數(shù)字 Double Float Long Int Short Byte 沒(méi)有隱式拓寬轉(zhuǎn)換,但...
    YuanchaoLi閱讀 521評(píng)論 0 2
  • 在 Kotlin 中的變量始花、常量以及注釋多多少少和 Java 語(yǔ)言是有著不同之處的妄讯。下面詳細(xì)的介紹 Kotlin ...
    馳同學(xué)閱讀 873評(píng)論 0 2
  • 一、定義和目的 1.定義 Kotlin是一種針對(duì)Java平臺(tái)的新編程語(yǔ)言酷宵。Kotlin簡(jiǎn)潔亥贸、安全、務(wù)實(shí)浇垦,并且專注于...
    TomyZhang閱讀 356評(píng)論 0 0
  • Kotlin概述 Kotlin是由開發(fā)過(guò)IntelliJ IDEA炕置、Android Studio、PyCharm等...
    OKmyself閱讀 11,363評(píng)論 2 12