Kotlin基礎(chǔ)

前言

2017年谷歌IO大會(huì)宣布,將Android開發(fā)的官方語言更換為Kotlin,作為Android開發(fā)有必要對(duì)Kotlin語言進(jìn)行了解使用犁罩。

Kotlin語言提供了與java語言100%的互操作性插佛,將現(xiàn)代語言的優(yōu)勢(shì)帶入Android開發(fā)的過程當(dāng)中,并引入了內(nèi)聯(lián)函數(shù)以及lambda表達(dá)式等使得代碼更精簡(jiǎn)剑勾,運(yùn)行效率更高埃撵。

對(duì)于熟悉java開發(fā)的開發(fā)人員來說,kotlin的學(xué)習(xí)成本會(huì)大大降低虽另,并且以及在很多大型互聯(lián)網(wǎng)公司進(jìn)入應(yīng)用階段暂刘。

1. 基礎(chǔ)類型

Kotlin所支持的基礎(chǔ)數(shù)據(jù)類型包括:數(shù)字、字符捂刺、布爾谣拣、數(shù)組募寨、字符串。

1.1 數(shù)字

與java當(dāng)中的數(shù)字類型相似森缠,kotlin內(nèi)置的數(shù)字類型包括

類型 字節(jié)
Double 64
Float 32
Long 64
Int 32
Short 16
Byte 8

字面值常量包括

類型 表示
十進(jìn)制 123
二進(jìn)制 0b00001011
十六進(jìn)制 0x0F

對(duì)于各個(gè)類型之間的轉(zhuǎn)換與java有所不同拔鹰,在java當(dāng)中,當(dāng)小類型向大的類型賦值時(shí)會(huì)發(fā)生隱式轉(zhuǎn)換:

    int a = 123;
    long b = a;
    System.out.println(a==b);//輸出true

但是在kotlin當(dāng)中不存在這種隱式轉(zhuǎn)換贵涵,在涉及到這種類型轉(zhuǎn)換的地方列肢,使用顯示轉(zhuǎn)換。

    val a:Int = 123
    val b:Long = a //報(bào)錯(cuò)
    val b:Long = a.toLong()

1.2 字符

字符類型使用Char類型標(biāo)識(shí)宾茂。

    val c:Char = '0'
    val str:String = c.toString()//Char類型轉(zhuǎn)換為String  str = "0"
    val i:Int = c.toInt()//Char類型轉(zhuǎn)換為Int,轉(zhuǎn)換為字符對(duì)應(yīng)Unicode編碼值  i=48

1.3 布爾

與java中的布爾類型相似瓷马,布爾類型只有兩個(gè)值true或者false,使用Boolean類型表示:

    val b1:Boolean = true
    val b2:Boolean = false

1.4 數(shù)組

數(shù)組類型使用Array來表示,系統(tǒng)提供了數(shù)字類型的getset方法以及size屬性等跨晴。創(chuàng)建一個(gè)數(shù)組的方式有兩種:

  • 使用arrayOf欧聘、intArrayOfshortArrayOf函數(shù)初始化數(shù)組坟奥。
  • 使用Array的構(gòu)造函數(shù)初始化數(shù)組树瞭。
   //方法1 使用`arrayOf`函數(shù)初始化數(shù)組
    val array1 = arrayOf(1, 2, 3)
    //遍歷數(shù)組的方法后續(xù)介紹
    for(i in array1.indices){
        println("array1[$i] = ${array1.get(i)}")
    }

    val array2:IntArray = intArrayOf(100,200,300)
    for(i in array2.indices){
        println("array2[$i] = ${array2.get(i)}")
    }
    //方法2 使用`Array`的構(gòu)造函數(shù)初始化數(shù)組
    //Array(size: Int, init: (Int) -> T)
    val array3 = Array(5) {
        it*it
    }
    for(i in array3.indices){
        println("array3[$i] = ${array3.get(i)}")
    }

輸出結(jié)果

array1[0] = 1 array1[1] = 2 array1[2] = 3
array2[0] = 100 array2[1] = 200 array2[2] = 300
array3[0] = 0 array3[1] = 1 array3[2] = 4 array3[3] = 9 array3[4] = 16

1.5 字符串

字符串使用String類型表示。與java中的字符串類似可以使用+符號(hào)進(jìn)行字符串鏈接

    val str = "aaaa"
    println(str+"bbbb") //輸出aaaabbbb

對(duì)于字符串換行爱谁,kotlin提供兩種方式

  • 雙引號(hào)"\n的方式
  • 三引號(hào)使用原始字符串"""
val str = "aaaa\nbbbb"
    val str2 = """aaaa
        |bbbb
    """.trimMargin() //trimMargin() 方法去除前導(dǎo)空格
    //默認(rèn) | 用作邊界前綴晒喷,但你可以選擇其他字符并作為參數(shù)傳入,比如 trimMargin(">") 访敌。
    
//兩種方式輸出結(jié)果相同
aaaa
bbbb

kotlin提供了字符串模板凉敲,即字符串中含有少部分代碼。字符串模板的使用方式為$變量或者${表達(dá)式}的方式

    val str = "aa"
    println("變量 str 的值為$str,長(zhǎng)度為${str.length}")

//輸出結(jié)果
變量 str 的值為aa,長(zhǎng)度為2

2. 控制流

2.1 if

kotlin當(dāng)中寺旺,if是一個(gè)含有返回值的表達(dá)式爷抓,除了像java當(dāng)中作為語句處理之外,在每個(gè)分支語塊下的最后的表達(dá)式將作為返回值返回阻塑。

    //val代表不可變參數(shù)蓝撇,類似于java的final,var代表可變參數(shù)
    var a = 1
    if (a in 1..10) {
        println("a is in 1..10")
    } else {
        println("unkonwn")
    }

    a = 15
    //判斷a的范圍并返回
    val result = if (a in 1..10) {
        "a is in 1..10"
    } else if (a in 11..20) {
        "a is in 11..20"
    } else {
        "unkonwn"
    }
    println(result)
    
//結(jié)果輸出
a is in 1..10
a is in 11..20

2.2 when

when表達(dá)式類似于java中的switch操作符陈莽。與if同樣渤昌,既可以作為語句也可以作為表達(dá)式返回值。

    val i = 1
    when(i){
        1 -> println("branch 1")
        2 -> println("branch 2")
        3 -> println("branch 3")
        4 -> println("branch 4")
        else -> println("unknown")
    }
    
    val result = when(i){
        1 ->"branch 1"
        2 -> "branch 2"
        3 -> "branch 3"
        4 -> "branch 4"
        else -> "unknown"
    }
    println("result is $result")
//輸出結(jié)果
branch 1
result is branch 1

相比于如果對(duì)于多種分支返回相同的結(jié)果走搁,可以集中處理独柑,并且可以使用表達(dá)式作為分支條件,而不是變量私植。

    when(i){
        //如果是1或者2
        1,2 -> println("i is 1 or 2")
        //如果i的取值在3--10
        in 3..10 -> println("i is in 3--10")
        //如果i是int類型
        is Int -> println("i is Int")
        else -> println("unknown")
    }

2.3 for

for循環(huán)可以對(duì)任何提供了迭代器的對(duì)象進(jìn)行遍歷忌栅。
遍歷一個(gè)數(shù)組

    //使用索引遍歷數(shù)組
    for(i in array1.indices){
        println("array1[$i] = ${array1.get(i)}")
    }
    //使用庫函數(shù)遍歷
    for ((index, value) in array1.withIndex()) { 
        println("the element at $index is $value")
    }

遍歷數(shù)字區(qū)間

//遍歷1--3
for (i in 1..3) { println(i)
}
//遍歷6--0 步長(zhǎng)為2
for (i in 6 downTo 0 step 2) {
println(i) }

遍歷map集合

    for((key,value) in values){
        println("key is $key,value is $value")
    }

2.4 while

while語法的用法與java中基本相同

    var index = 0
    while (index < items.size) {
        println("item at $index is ${items[index]}")
        index++
    }

2.5 標(biāo)簽

Kotlin提供了三種標(biāo)簽:returnbreakcontinue曲稼,其作用方式與java中相同

  • return:從包含它的函數(shù)中返回索绪。
  • break:從包含它的循環(huán)中返回湖员。
  • continue:繼續(xù)下一次循環(huán)。
    除了正常類似java中的調(diào)用方式外者春,還可以配合標(biāo)簽使用破衔。標(biāo)簽的格式為標(biāo)識(shí)符后跟 @ 符號(hào)清女,例如:abc@ 钱烟、loop@。
    代碼說明
fun main(args: Array<String>) {
    var result = 0
    loop@ for (i in 0..10) {
        for (j in 0..10) {
            if (i == 2 && j == 2) {
                //如果不加標(biāo)簽跳出j的循環(huán)嫡丙,加標(biāo)簽后跳出loop指定循環(huán)
                println("break to loop")
                break@loop
            }else{
                result++
            }
        }
    }
    println("result is $result")
}

//結(jié)果輸出
break to loop
result is 24

使用標(biāo)簽跳出lambda表達(dá)式

fun foo() {
    listOf(1, 2, 3, 4, 5).forEach {
        // 在lambda表達(dá)式中不使用標(biāo)簽拴袭,直接退出到函數(shù)調(diào)用者,即退出最近的一個(gè)fun()
        if (it == 3) return 
        print(it) 
    }
}

fun foo() {
    listOf(1, 2, 3, 4, 5).forEach lit@{
        // 使用標(biāo)簽可以退出當(dāng)前l(fā)ambda表達(dá)式曙博,如果不顯式添加標(biāo)簽拥刻,默認(rèn)標(biāo)簽與函數(shù)名相同
        if (it == 3) return@lit 
        print(it) 
    }
}

也可以在標(biāo)簽處添加返回值如:return@loop 1

3. 類

kotlin當(dāng)中的類使用關(guān)鍵字class聲明class A{},類中的成員包括:構(gòu)造函數(shù)和初始化塊父泳、屬性般哼、函數(shù)嵌套類和內(nèi)部類以及對(duì)象聲明惠窄。

3.1 構(gòu)造函數(shù)和初始化塊

kotlin的構(gòu)造函數(shù)與java不同蒸眠,分為主構(gòu)造函數(shù)和次構(gòu)造函數(shù),構(gòu)造函數(shù)使用關(guān)鍵字constructor聲明杆融。主構(gòu)造函數(shù)內(nèi)不能有任何代碼楞卡,其聲明方式如下:

/**
 * 在主構(gòu)造函數(shù)中初始化屬性,當(dāng)不聲明構(gòu)造函數(shù)時(shí)會(huì)默認(rèn)有一個(gè)無參構(gòu)造函數(shù)
 * class ClassName public constructor()
 */
class Person constructor(firstName: String) 
 
//也可以在構(gòu)造函數(shù)中對(duì)參數(shù)進(jìn)行賦值操作
class TestClass constructor(val key: String = "a", val value: String = "b"):ParentClass(key)

如果主構(gòu)造函數(shù)沒有任何注解或者可?性修飾符脾歇,可以省略這個(gè) constructor 關(guān)鍵字蒋腮。

class Person(firstName: String)

在類內(nèi)部可以聲明次構(gòu)造函數(shù),同樣使用constructor關(guān)鍵字聲明藕各。

    //每個(gè)次構(gòu)造函數(shù)都需要委托給主構(gòu)造函數(shù)池摧,委托方式有兩種
    //1.直接委托給主構(gòu)造函數(shù)
    constructor(key: String, value: String, param1: String) : this(key, value){
        println("constructor 1")
    }

    //2. 通過其他次構(gòu)造函數(shù)委托
    constructor(key: String, value: String, param1: String, param2: String) : this(key, value, param1) {
        println("constructor 2")
    }

構(gòu)造函數(shù)內(nèi)部不能包含代碼,類的初始化工作就放在了初始化代碼塊中激况。初始化代碼塊使用init表示作彤。

    //初始化代碼可以放在init關(guān)鍵字表示的代碼塊中處理
    //在類對(duì)象初始化時(shí),與類屬性順序執(zhí)行
    //實(shí)際上這部分代碼會(huì)成為主構(gòu)造函數(shù)的一部分,在次函數(shù)委托給主函數(shù)之后誉碴,與次函數(shù)第一行執(zhí)行這部分代碼
    val classKey = "the Key is : $key".also(::println)

    init {
        println("first init code in this")
    }

    val classValue = "the value is : $value".also(::println)

    init {
        println("second init code in this")
    }

3.2 接口

Kotlin的接口使用interface來表示宦棺。

interface Base {
    val message: String
    fun print()
}

/**
 * 實(shí)現(xiàn)接口
 */
class BaseImpl(val x: Int) : Base {
    override val message = "nnnn"
    override fun print() {
        println(message)
    }
}

3.3 繼承

Kotlin中所有類都有一個(gè)共同的超類Any,這對(duì)于沒有超類型聲明的類是默認(rèn)超類黔帕。如果想要顯式的聲明一個(gè)超類代咸,我們使用open關(guān)鍵字。默認(rèn)情況下Kotlin中的類都是不允許被繼承的成黄,只有用open關(guān)鍵字聲明的類才允許被繼承呐芥。

/**
 * open關(guān)鍵字與final關(guān)鍵字相反逻杖,它允許其他類對(duì)它進(jìn)行繼承。
 * 默認(rèn)情況下Kotlin中的類都是public final類型
 */
open class ParentClass(key: String)

如果子類沒有主構(gòu)造函數(shù)思瘟,則子類的所有構(gòu)造函數(shù)都必須使用super關(guān)鍵字初始化其基類型荸百,或委托給另一個(gè)構(gòu)造函數(shù)做到這一點(diǎn)。如果子類有主構(gòu)造函數(shù)滨攻,子類可以在繼承時(shí)直接使用父類主構(gòu)造函數(shù)初始化够话。

open class ParentClass(key: String) {
     constructor(key: String, value: String) : this(key) {
        println("parent constructror 1")
    }
}

//當(dāng)子類沒有主構(gòu)造函數(shù)時(shí),可以在子類的構(gòu)造方法后繼承不同的父構(gòu)造方法
class Child: ParentClass {
    constructor(key: String, value: String):super(key)
    //委托給不同的父構(gòu)造函數(shù)
    constructor(key: String, value: String, param1: String) : super(key, value) {
        println("constructor 1")
    }
}

//當(dāng)子類有構(gòu)造函數(shù)時(shí)光绕,需要繼承父類的構(gòu)造函數(shù)
class Child(key: String) : ParentClass(key) {
    //子類有主構(gòu)造函數(shù)女嘲,委托給子類的主構(gòu)造函數(shù)進(jìn)行。
    constructor(key: String, value: String):this(key)

    constructor(key: String, value: String, param1: String) : this(key, value) {
        println("constructor 1")
    }
}

對(duì)于父類的屬性和方法诞帐,如果允許子類對(duì)其進(jìn)行覆蓋欣尼,則需要用open關(guān)鍵字顯式標(biāo)注。在子類中使用override關(guān)鍵字覆蓋停蕉。

open class ParentClass(key: String) {
    /**
     * 聲明為open關(guān)鍵字表示其可以被子類復(fù)寫愕鼓,如果不聲明open關(guān)鍵字,默認(rèn)為public final
     * 使用var可以覆蓋val屬性慧起,反之則不可以菇晃,因?yàn)関al屬性默認(rèn)只有g(shù)etter方法,而var有g(shù)etter和setter方法完慧。
     */
    open val a = "aaa".also {
        println(it)
    }
    /**
     * 聲明為open關(guān)鍵字表示其可以被子類復(fù)寫谋旦,如果不聲明open關(guān)鍵字,默認(rèn)為public final
     */
    open fun method(param:String) {
        println("......")
    }
}

class Child : ParentClass {
    override val a = "bbb".also { println(it) }
    override fun method(param: String) {
        super.method(param)
        println("[[[[[[")
    }
}

新建子類對(duì)象時(shí)屈尼,會(huì)先完成基類的初始化操作册着,然后在初始化子類。

在 Kotlin 中脾歧,實(shí)現(xiàn)繼承由下述規(guī)則規(guī)定:如果一個(gè)類從它的直接超類繼承相同成員的多個(gè)實(shí)現(xiàn)甲捏,它必須覆蓋這個(gè)成員并提供其自己的實(shí)現(xiàn)(也許用繼承來 的其中之一)。為了表示采用從哪個(gè)超類型繼承的實(shí)現(xiàn)鞭执,我們使用由尖括號(hào)中超類型名限定的 super司顿,如 super<Base> :

open class A {
    open fun f() { print("A") } fun a() { print("a") }
}
interface B {
    open fun f() { print("B") } // 接口成員默認(rèn)就是“open”的 fun b() { print("b") }
}
class C : A(),B {
    // 編譯器要求覆蓋 f(): 
    override fun f() {
        super<A>.f() // 調(diào)用 A.f()
        super<B>.f() // 調(diào)用 B.f() 
    }
}

3.4 伴生對(duì)象

Kotlin當(dāng)中并沒有靜態(tài)方法,也就沒有辦法使用單例等不需要?jiǎng)?chuàng)建類的實(shí)例兄纺,通過類名就可以訪問的函數(shù)大溜。這時(shí)候就用到了伴生對(duì)象。伴生對(duì)象用companion關(guān)鍵字標(biāo)記估脆。

class InstanceClass {
    companion object A{
        fun create(): InstanceClass = InstanceClass()
    }
}

fun main(args:Array<String>){
    val instance = InstanceClass.create()
}

這里需要注意的是钦奋,這種用法看起來像是類似于Java中的靜態(tài)方法,實(shí)際上是相當(dāng)于聲明一個(gè)靜態(tài)內(nèi)部類A,然后調(diào)用靜態(tài)內(nèi)部類中的方法。

3.5 其他類

數(shù)據(jù)類

Kotlin中使用data標(biāo)記的類為數(shù)據(jù)類付材。

data class User(val name: String, val age: Int)

為了確保生成的代碼的一致性和有意義的行為朦拖,數(shù)據(jù)類必須滿足以下要求:

  • 主構(gòu)造函數(shù)需要至少有一個(gè)參數(shù);
  • 主構(gòu)造函數(shù)的所有參數(shù)需要標(biāo)記為 val 或 var ;
  • 數(shù)據(jù)類不能是抽象、開放厌衔、密封或者內(nèi)部的;
  • (在1.1之前)數(shù)據(jù)類只能實(shí)現(xiàn)接口璧帝。

密封類

密封類用來表示受限的類繼承結(jié)構(gòu):當(dāng)一個(gè)值為有限集中的類型、而不能有任何其他類型時(shí)富寿。

要聲明一個(gè)密封類睬隶,需要在類名前面添加 sealed 修飾符。雖然密封類也可以有子類作喘,但是所有子類都必須在與密封類自身相同的文件中聲明理疙。

sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr() 
object NotANumber : Expr()

//使用when區(qū)分不同的情況。
fun eval(expr: Expr): Double = when(expr) { 
    is Const -> expr.number
    is Sum -> eval(expr.e1) + eval(expr.e2) 
    NotANumber -> Double.NaN
    // 不再需要 `else` 子句泞坦,因?yàn)槲覀円呀?jīng)覆蓋了所有的情況 
}

嵌套類和內(nèi)部類

一個(gè)類可以定義到另一個(gè)類當(dāng)中

class Outer {
private val bar: Int = 1 
    class Nested {
        fun foo() = 2 
    }
}
val demo = Outer.Nested().foo() // == 2

使用inner標(biāo)記為內(nèi)部類,并可以訪問外部類的成員砖顷。

class Outer {
    private val bar: Int = 1 
    inner class Inner {
        fun foo() = bar 
    }
}
val demo = Outer().Inner().foo() // == 1

匿名內(nèi)部類的實(shí)現(xiàn)采用對(duì)象表達(dá)式的方式

window.addMouseListener(object: MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) {
    // ......
    }
    override fun mouseEntered(e: MouseEvent) { // ......
    } 
})

如果對(duì)象是函數(shù)式Java接口(即具有單個(gè)抽象方法的Java接口)的實(shí)例贰锁,你可以使用帶接口類型前綴的lambda表達(dá)式創(chuàng)建它:

val listener = ActionListener { println("clicked") }

枚舉類

kotlin中的每一個(gè)枚舉常量都是一個(gè)對(duì)象,用逗號(hào)分隔

enum class Direction { 
    NORTH, SOUTH, WEST, EAST
}

//帶有初始化信息的枚舉類
enum class EnumClass(var x:Int,var y:Int){
    NOUTH(0,0),SOUTH(90,90),WEST(180,180),EAST(270,270);

   init {
        print("init $this  ")
        x = x+1
        y = -y
    }
}
//調(diào)用枚舉類型
fun main(args:Array<String>){
    //在初次調(diào)用枚舉類型時(shí)初始化所有枚舉常量
    println(EnumClass.EAST)
    println(EnumClass.NOUTH)
}

//輸出結(jié)果
init NOUTH  init SOUTH  init WEST  init EAST  EAST
NOUTH

3.6 擴(kuò)展

java當(dāng)中我們經(jīng)常會(huì)寫一些Utils滤蝠,比如說StringUtils等來做一些對(duì)String的擴(kuò)展功能封裝豌熄。在Kotlin中可以直接對(duì)類的功能進(jìn)行擴(kuò)展,而無需繼承該類物咳。

我們對(duì)MutableList<Int>類進(jìn)行擴(kuò)展函數(shù)锣险。

//擴(kuò)展函數(shù),擴(kuò)展方法convert()實(shí)現(xiàn)對(duì)列表的倒置
fun MutableList<Int>.convert(){
    for(i in 0..(this.size/2)){ // “this”對(duì)應(yīng)該列表
        val tmp = this[i] 
        this[i] = this[this.size-1-i]
        this[this.size-1-i] = tmp
    }
}

//擴(kuò)展屬性览闰,獲取最后一個(gè)元素芯肤。
val <T> List<T>.lastData: T get() = this[this.size-1]

fun main(args: Array<String>) {
    val list = mutableListOf(1, 2, 3, 4, 5)
    println(list.lastData)
    list.convert()
    println(list)
}

//輸出結(jié)果
5
[5, 4, 3, 2, 1]

注意:當(dāng)擴(kuò)展函數(shù)與成員函數(shù)相同時(shí),在調(diào)用時(shí)總是取成員函數(shù)压鉴。

3.7 委托

委托是kotlin實(shí)現(xiàn)的繼承的一種替代方式,委托的內(nèi)容通過by關(guān)鍵字實(shí)現(xiàn)崖咨。

/**
 * 定義Base接口
 */
interface Base {
    val message: String
    fun print()
}

/**
 * 對(duì)Base接口的實(shí)現(xiàn)
 */
class BaseImpl(val x: Int) : Base {
    override val message = "base message"
    override fun print() {
        println(message)
    }
}

/**
 * 委托給Base類的具體實(shí)現(xiàn)
 */
class Dervied(b: Base) : Base by b {
    //調(diào)用委托對(duì)象b時(shí),不會(huì)訪問到
    override val message = "dervied message"
}

fun main(args: Array<String>) {
    val b = BaseImpl(1)
    //因?yàn)閷rint()函數(shù)的處理委托給Base b來處理油吭,所以當(dāng)訪問不到Dervied()里面的內(nèi)容時(shí)
    //直接執(zhí)行b的print方法击蹲,輸出`base message`
    val a = Dervied(b)
    a.print()
    //如果有對(duì)b中的內(nèi)容進(jìn)行重寫,則優(yōu)先使用Dervied中的變量或方法
    println(a.message)
}

//輸出結(jié)果
base message
dervied message

委托屬性

Kotlin本身支持委托屬性婉宰,同樣通過by關(guān)鍵字實(shí)現(xiàn)歌豺。

val str:String by Delegate()

實(shí)現(xiàn)委托后屬性對(duì)應(yīng)的get()set()會(huì)被委托給by后面的getValue()setValue()方法,為此接收委托的類需要實(shí)現(xiàn)對(duì)應(yīng)的getValue()setValue()方法.(val 可以只實(shí)現(xiàn)getValue方法心包,因?yàn)関al屬性是只讀的)

class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, thank you for delegating '${property.name}' to me!" 
    }
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { 
        println("$value has been assigned to '${property.name}' in $thisRef.")
    } 
    //thisRef表示被委托的對(duì)象自身
    //property保存被委托對(duì)象對(duì)于自身的描述
    //value表示被賦予的新值类咧,因?yàn)閟tr是String類型,所以這里value只接收String類型
}

Kotlin提供了幾種標(biāo)準(zhǔn)委托:延遲屬性(lazy)可觀察屬性(Observable).

延遲屬性(lazy)接收一個(gè)lambda表達(dá)式作為參數(shù)轮听,第一次調(diào)用 get() 會(huì)執(zhí)行已傳遞給
lazy() 的 lambda 表達(dá)式并記錄結(jié)果骗露,后續(xù)調(diào)用 get() 只是返回記錄的結(jié)果。

    //String為lazy后lambda表達(dá)式最后的值,在第一次調(diào)用到lazy變量時(shí)才初始化并調(diào)用lazy后lambda表達(dá)式血巍,之后調(diào)用則直接返回最后的值萧锉。
    val lazy: String by lazy {
        println("computed!")
        "Hello"
    }
    
    fun main(args: Array<String>) {
        println(a.lazy)
        println(a.lazy)
    }
    //輸出結(jié)果
    computed述寡!
    Hello
    Hello

可觀察屬性(Observable)接受兩個(gè)參數(shù):初始值修改時(shí)處理程序柿隙。每當(dāng)變量的值在發(fā)生改變時(shí)都會(huì)交給修改時(shí)處理程序處理。

    // Delegates.observable標(biāo)準(zhǔn)委托鲫凶,在修改屬性值時(shí)調(diào)用
    // 接收兩個(gè)參數(shù)分別為初始化的值以及對(duì)修改時(shí)的監(jiān)聽(類似Android中的handler)
    // 監(jiān)聽方法中的三個(gè)參數(shù)分別為修改的屬性禀崖、舊值、新值
    var observable: String by Delegates.observable("initValue") { prop, old, new ->
            println("$old ->> $new")
    }

    // Delegates.vetoable標(biāo)準(zhǔn)委托螟炫,在修改屬性值時(shí)調(diào)用
    // 接收兩個(gè)參數(shù)分別為初始化的值以及對(duì)修改時(shí)的監(jiān)聽(類似Android中的handler)波附,并判斷是否對(duì)屬性的值進(jìn)行修改
    // 返回true時(shí)對(duì)值進(jìn)行修改操作,返回false時(shí)不對(duì)值進(jìn)行修改
    // 監(jiān)聽方法中的三個(gè)參數(shù)分別為修改的屬性昼钻、舊值掸屡、新值
    var observable1: String by Delegates.vetoable("initValue") { prop, old, new ->
        println("$old ->> $new")
        new.equals("bb")
    }
    
    fun main(args: Array<String>) {
        a.observable = "111"
        a.observable = "222"

        println(a.observable1)
        a.observable1 = "bb"
        println(a.observable1)
        a.observable1 = "aa"
        println(a.observable1)
    }
    
//輸出結(jié)果
initValue ->> 111
111 ->> 222
initValue1
initValue1 ->> bb
bb
bb ->> aa
bb

4. 其他

命名參數(shù)

如果需要調(diào)用的方法有很多參數(shù),就可以使用命名參數(shù)的方式然评。

//函數(shù)定義
fun reformat(str: String,
    normalizeCase: Boolean = true,
    upperCaseFirstLetter: Boolean = true,
    divideByCamelHumps: Boolean = false, 
    wordSeparator: Char = ' ') {
        ...... 
    }
//調(diào)用時(shí)可以只修改必要的參數(shù)仅财,對(duì)于默認(rèn)參數(shù)不進(jìn)行修改的可以不須修改
reformat(str, wordSeparator = '_')

高階函數(shù)

Kotlin支持將函數(shù)作為返回值或者函數(shù)使用,作為函數(shù)類型時(shí)碗淌,使用一個(gè)圓括號(hào)括起來的參數(shù)類型列表以及一個(gè)返回類型:(A, B) -> C 表示接受類型分別為 A 與 B 兩個(gè)參數(shù)并返回一個(gè) C 類 型值的函數(shù)類型盏求。

函數(shù)類型可以有一個(gè)額外的接收者類型,它在表示法中的點(diǎn)之前指定:類型 A.(B) -> C 表示可以在 A 的接收者對(duì)象上以一個(gè) B 類型參數(shù)來調(diào) 用并返回一個(gè) C 類型值的函數(shù)亿眠。帶有接收者的函數(shù)字面值通常與這些類型一起使用碎罚。

    //帶接收者類型為String類型,Int為調(diào)用類型即times的類型
    val repeat: String.(Int) -> String = { times -> repeat(times) }
    //帶與不帶接收者的函數(shù)類型非字面值可以互換缕探,其中接收者可以替代第一個(gè)參數(shù)魂莫,反之亦然
    val twoParameters: (String, Int) -> String = repeat

既然函數(shù)可以作為參數(shù)使用,那么帶有函數(shù)類型的函數(shù)被稱為高階函數(shù)爹耗。

    //定義函數(shù)example耙考,它的參數(shù)是一個(gè)函數(shù)類型的參數(shù)
    fun example(times: Int, computeFoo: () -> String) {
    //example內(nèi)部執(zhí)行times次computeFoo函數(shù),并輸出執(zhí)行結(jié)果
        var i = 0
        while(i<times){
            println("${computeFoo()}")
            i++
        }
    }
    
    fun main(agrs: Array<String>) {
        //調(diào)用example方法
        a.example(2) {
        "aaa"
        }
    }
//輸出結(jié)果
aaa
aaa

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

kotlin引入了內(nèi)聯(lián)函數(shù),內(nèi)聯(lián)函數(shù)就是在程序編譯時(shí),編譯器將程序中出現(xiàn)的內(nèi)聯(lián)函數(shù)的調(diào)用表達(dá)式用內(nèi)聯(lián)函數(shù)的函數(shù)體來直接進(jìn)行替換眼溶,用以消除高階函數(shù)以及函數(shù)類型的不必要開銷毒费。

    //定義內(nèi)聯(lián)函數(shù)synchronized用于實(shí)現(xiàn)同步屈呕,它接收兩個(gè)參數(shù)鎖Lock以及同步的內(nèi)容action()
   inline fun <T> synchronized(lock:Lock,action:()->T):T{
        lock.lock()
        try {
            return action()
        }
        finally {
            lock.unlock()
        }
    }
   val l=Lock()
   //類似于java中的調(diào)用,使用synchronized同步代碼塊
   synchronized(l){
        ...
   }

inline修飾符也可以標(biāo)注獨(dú)立的屬性訪問器

val foo: Foo
    inline get() = Foo()
var bar: Bar get() = ......
    inline set(v) { ...... }

解構(gòu)聲明

解構(gòu)聲明可以幫助我們把單一對(duì)象分解成多個(gè).

//定義Persion數(shù)據(jù)類以及其主構(gòu)造函數(shù) 
//當(dāng)聲明為數(shù)據(jù)類時(shí)不需要提供主構(gòu)造函數(shù)的componentN方法
data class Person(var name:String,var age:Int){
    var address = ""
    //定義次構(gòu)造函數(shù)
    constructor(name:String,age:Int,address:String):this(name,age){
        this.address = address
    }
    //定義componentN方法用于解構(gòu)聲明
    operator fun component3():String{
        return this.address
    }
}

fun main(args:Array<String>){
    //聲明Person類
    val person = Person("小明",18,"北京市朝陽區(qū)")
    //解構(gòu)聲明調(diào)用
    //結(jié)構(gòu)聲明在編譯時(shí)會(huì)分解成
    //String name = person.component1();
    //int age = person.component2();
    //String address = person.component3();三個(gè)步驟來做薛夜,所以需要在類中提供這三個(gè)方法
    val (name,age,address) = person
}

解構(gòu)聲明也可以在for循環(huán)中使用

//map使用解構(gòu)聲明
var map = HashMap<String, Person>()
    map.put("1", personA)
    map.put("2", personB)
    map.put("3", personC)
    map.put("4", personD)
    map.put("5", personE)
    for ((key, value) in map) {
        println("key: $key, value: $value")
    }

//對(duì)象使用解構(gòu)聲明
var personA: Person = Person("Door", 22, "ShanDong")
var personB: Person = Person("Green", 30, "BeiJing")
var personC: Person = Person("Dark", 23, "YunNan")
var personD: Person = Person("Tool", 26, "GuanDong")
var personE: Person = Person("Mark", 24, "TianJin")
var pers = listOf(personA, personB, personC, personD, personE)
for ((name, age) in pers) {
    println("name: $name, age: $age")
}

This表達(dá)式

為了表示當(dāng)前的 接收者 我們使用 this 表達(dá)式:

  • 在類的成員中袱蚓,this 指的是該類的當(dāng)前對(duì)象枚碗。
  • 在擴(kuò)展函數(shù)或者帶接收者的函數(shù)字面值中逾一,this 表示在點(diǎn)左側(cè)傳遞的接收者參數(shù)。
    要訪問來自外部作用域的this(一個(gè)類 或者擴(kuò)展函數(shù)肮雨,或者帶標(biāo)簽的帶接收者的函數(shù)字面值)我們使用 this@label 遵堵,其中 @label 是一個(gè)代指 this 來源的標(biāo)簽:
class A { // 隱式標(biāo)簽 @A
    inner class B { // 隱式標(biāo)簽 @B
        fun Int.foo() { // 隱式標(biāo)簽 @foo val a = this@A // A 的 this val b = this@B // B 的 this
            val c = this // foo() 的接收者,一個(gè) Int
            val c1 = this@foo // foo() 的接收者怨规,一個(gè) Int
            val funLit = lambda@ fun String.() { 
                val d = this // funLit 的接收者
            }
            val funLit2 = { s: String ->
                // foo() 的接收者陌宿,因?yàn)樗?lambda 表達(dá)式 // 沒有任何接收者
                val d1 = this
            } 
        }
    } 
}

結(jié)尾

本篇內(nèi)容只是簡(jiǎn)單介紹了kotlin的一些常規(guī)用法,借鑒于kotlin中文文檔波丰,如果需要系統(tǒng)的學(xué)習(xí)這門語言壳坪,建議閱讀官方文檔。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末掰烟,一起剝皮案震驚了整個(gè)濱河市爽蝴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌媚赖,老刑警劉巖霜瘪,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異惧磺,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)捻撑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門磨隘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人顾患,你說我怎么就攤上這事番捂。” “怎么了江解?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵设预,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我犁河,道長(zhǎng)鳖枕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任桨螺,我火速辦了婚禮宾符,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘灭翔。我一直安慰自己魏烫,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著哄褒,像睡著了一般稀蟋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上呐赡,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天退客,我揣著相機(jī)與錄音,去河邊找鬼罚舱。 笑死井辜,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的管闷。 我是一名探鬼主播粥脚,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼包个!你這毒婦竟也來了刷允?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤碧囊,失蹤者是張志新(化名)和其女友劉穎树灶,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體糯而,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡天通,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了熄驼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片像寒。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖瓜贾,靈堂內(nèi)的尸體忽然破棺而出诺祸,到底是詐尸還是另有隱情,我是刑警寧澤祭芦,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布筷笨,位于F島的核電站,受9級(jí)特大地震影響龟劲,放射性物質(zhì)發(fā)生泄漏胃夏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一咸灿、第九天 我趴在偏房一處隱蔽的房頂上張望构订。 院中可真熱鬧,春花似錦避矢、人聲如沸悼瘾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽亥宿。三九已至卸勺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間烫扼,已是汗流浹背曙求。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留映企,地道東北人悟狱。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像堰氓,于是被迫代替她去往敵國(guó)和親挤渐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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