kotlin 學(xué)習(xí)第一步

Kotlin 基礎(chǔ)知識(shí)

一,Using Kotlin for Android Development

? 1.1 使用Kotlin做Android開發(fā)

Kotlin 是非常適合用于開發(fā)Android應(yīng)用的,它給Android平臺(tái)帶來(lái)了新語(yǔ)言的所有優(yōu)點(diǎn)卻不增加任何新的限制:

兼容性:Kotlin完全兼容JDK6俐筋,所以Kotlin可以在老舊的Android設(shè)備上運(yùn)行不出問(wèn)題捞镰。AndroidStudio完全支持Kotlin,編譯系統(tǒng)也兼容Kotlin凿蒜。

性能:因?yàn)榉浅O嗨频淖止?jié)碼結(jié)構(gòu)谐腰,Kotlin應(yīng)用運(yùn)行速度和Java一樣快。因?yàn)镵otlin支持內(nèi)聯(lián)函數(shù)框弛,當(dāng)時(shí)用lambdas時(shí)辛辨,通常比用Java代碼運(yùn)行更快。

互通性:Kotlin能100%與Java相互協(xié)作功咒,能使用所有的Android庫(kù)愉阎,包括注解處理庫(kù),所以像databinding力奋,dagger這些庫(kù)都能使用。

占用空間:Kotlin的運(yùn)行時(shí)庫(kù)非常簡(jiǎn)潔幽七,當(dāng)時(shí)用混淆的時(shí)候還會(huì)縮減到更小景殷,只會(huì)增加幾百個(gè)方法,只增加apk不到100k的體積澡屡。

編譯時(shí)間:Kotlin支持高效的增量編譯猿挚,所以當(dāng)項(xiàng)目已經(jīng)編譯過(guò),再進(jìn)行額外的編譯時(shí)驶鹉,這種增量編譯可以像Java一樣快绩蜻,甚至更快。

學(xué)習(xí)曲線:對(duì)于Java開發(fā)者來(lái)說(shuō)室埋,使用Kotlin非常簡(jiǎn)單办绝。Java代碼自動(dòng)化轉(zhuǎn)換成Kotllin代碼的Kotlin插件幫我們邁出了第一步。 Kotlin Koans 通過(guò)一系列的包含Kotlin主要特點(diǎn)的交互性習(xí)題給我們提供了指南姚淆。

1.2 用Kotlin 做Android開發(fā)樣例研究

Kotlin已經(jīng)被一些大企業(yè)接受孕蝉,其中部分企業(yè)分享了他們的經(jīng)驗(yàn):

Pinterest已經(jīng)成功地在他們?cè)掠脩袅?.5億的應(yīng)用中引進(jìn)了Kotlin。

Basecamp 的Android app 100%使用Kotlin代碼腌逢,他們表示Kotlin給他們帶來(lái)了編程的快樂(lè)降淮,以及工作質(zhì)量和效率的提升。

Keepsafe的app Lock app也已經(jīng)轉(zhuǎn)換成100%的Kotlin搏讶,這使得代碼行數(shù)減少30%佳鳖,方法數(shù)減少10%。

1.3 Android 開發(fā)工具

Kotlin團(tuán)隊(duì)一套工具用于開發(fā)媒惕,其中有超越了標(biāo)準(zhǔn)語(yǔ)言的特性:

Kotlin Android Extensions

是編譯器的拓展系吩,它使你免去在代碼中調(diào)用findViewById()方法,而是通過(guò)編譯生成吓笙。

Anko是一個(gè)庫(kù)淑玫,它提供了一組Android APIs和DSL的包裝,讓你用Kotlin代碼替換.xml文件。

1.4 下一步

下載安裝 Android Studio 3.0絮蒿,它提供了Kotlin支持尊搬,安裝即可使用。

按照 Getting Started with Android and Kotlin指導(dǎo)創(chuàng)建你的第一個(gè)Kotlin應(yīng)用土涝。

想要深入了解佛寿,可查看reference documenKotlin Koans

另一個(gè)很好的資源是一本書《 Kotlin for Android Developers》但壮,它一步一步地指導(dǎo)你創(chuàng)建一個(gè)Kotlin編寫的Android應(yīng)用冀泻。

看Google的樣例 sample projects written in Kotlin

二蜡饵,變量的聲明和使用

2.1 變量聲明

只讀變量

可理解為Java中常量弹渔,使用 val 關(guān)鍵字修飾

val a: Int = 1? //在聲明的時(shí)候直接賦值

val b = 2? // 類型是明確的,變量類型Int可省去

val c: Int? // 在聲明時(shí)沒(méi)有賦值溯祸,變量類型Int不可省去

c = 3? ? ? // 只可賦值一次肢专,此后c值不能再變

易變變量

可理解為Java中的普通變量樱报,用 var 關(guān)鍵字修飾芥丧,與 val 變量的區(qū)別是變量的值可變:

var a=0

a=1

a=3

成員變量

與Java一樣,通過(guò)對(duì)象.變量名的方式調(diào)用:

fun main(args: Array<String>) {

? ? print(Test().property)

}

class Test{

? ? var property="property"

}

靜態(tài)變量

用companion object{}包裹氛赐,與Java一樣通過(guò)類名.變量名的方式調(diào)用筷登,關(guān)于companion object后面會(huì)詳細(xì)講解:

fun main(args: Array<String>) {

? ? print(Test.com)

}

class Test{

? ? companion object{

? ? ? ? var com="com"

? ? }

}

頂級(jí)變量

在類的外部聲明剃根,可理解為Java中的靜態(tài)成員變量。

通過(guò)包名.變量名的方式來(lái)調(diào)用

樣例:

package a.b.c

var top = "top"

fun main(args: Array<String>) {

? ? print(a.b.c.top)

}

靜態(tài)變量與頂級(jí)變量的區(qū)別:

通過(guò)反編譯可以知道前方,其實(shí)他們不在一個(gè)類中狈醉。

當(dāng)文件中有頂級(jí)變量,編譯時(shí)會(huì)新生成一個(gè)[文件名+kt]的類镣丑,頂級(jí)變量就在其中舔糖。

2.2 Getters and Setters

我們先定義各種可見性的var類型成員變量:

? ? private var privateField = ""

? ? internal var internalField = ""

? ? protected var protectedField = ""

? ? var publicField = ""

然后反編譯看看對(duì)應(yīng)的java代碼:

? @NotNull

? public final String getInternalField$app() {

? ? ? return this.internalField;

? }

? public final void setInternalField$app(@NotNull String var1) {

? ? ? Intrinsics.checkParameterIsNotNull(var1, "<set-?>");

? ? ? this.internalField = var1;

? }

? @NotNull

? protected final String getProtectedField() {

? ? ? return this.protectedField;

? }

? protected final void setProtectedField(@NotNull String var1) {

? ? ? Intrinsics.checkParameterIsNotNull(var1, "<set-?>");

? ? ? this.protectedField = var1;

? }

? @NotNull

? public final String getPublicField() {

? ? ? return this.publicField;

? }

? public final void setPublicField(@NotNull String var1) {

? ? ? Intrinsics.checkParameterIsNotNull(var1, "<set-?>");

? ? ? this.publicField = var1;

? }

可知,protected和public會(huì)生成對(duì)應(yīng)的setter和getter方法莺匠,internal也生成了特殊的對(duì)應(yīng)方法金吗。

所以對(duì)于protected和public的成員變量,我們不能自己創(chuàng)建對(duì)應(yīng)的getter和setter方法趣竣,如:

var name:String?

fun setName(name:String){}

將會(huì)報(bào)錯(cuò):

Platform declaration clash: The following declarations have the same JVM signature (setName(Ljava/lang/String;)V)

Visibility Modifiers

對(duì)于getter

getter的可見性和變量的可見性相同摇庙,無(wú)需重復(fù)添加修飾符。非要多此一舉添加遥缕,如果和變量的修飾符不一樣卫袒,將會(huì)報(bào)錯(cuò),如:

var field: String = ""

private get? //報(bào)錯(cuò): Getter visibility must be the same as property visibility

對(duì)于setter

setter的可見性必須小于等于變量自身的可見性单匣,否則報(bào)錯(cuò)夕凝,如:

private var name=""

public set //error:Setter visibility must be the same or less permissive than property visibility

我們也可以自定義getter和setter方法

語(yǔ)法

<var <propertyName>[: <PropertyType>] [= <property_initializer>]

? ? [<getter>]

? ? [<setter>]

當(dāng)給變量賦值時(shí)會(huì)調(diào)用setter方法宝穗,調(diào)用變量得到的是getter中的返回值

樣例:

var name: String = "111"

? ? get() {

? ? ? ? return "{$field}"

? ? }

? ? set(value) {

? ? ? ? field = "[$value]"

? ? }

fun main(args: Array<String>) {

? ? println(name)

? ? name = "222"

? ? print(name)

}

結(jié)果:

{111}

{[222]}

三,方法的定義和使用

3.1 方法定義

定義語(yǔ)法:

fun? [方法名] ( [參數(shù)名] : [參數(shù)類型] ) : [返回類型]{

? ? ...

? ? return [返回值]

}

有返回值

fun multiply(x: Int, y:Int): Int {

? ? return x * y

}

也可以轉(zhuǎn)換為:

fun multiply(x: Int, y: Int): Int = x * y

還可以這樣:

var multiply = { x: Int, y: Int -> x * y }

無(wú)返回值

使用Unit代替返回類型:

fun log(msg: String): Unit {

? ? print(msg)

}

Unit也可以省去:

fun log(msg: String) {

? ? print(msg)

}

成員方法

與Java一樣通過(guò)對(duì)象.方法名的方式調(diào)用:

fun main(args: Array<String>) {

? ? Test().method()

}

class Test{

? ? fun method(){

? ? ? ? print("hello")

? ? }

}

靜態(tài)方法

和Java一樣通過(guò)類名.方法名的方式調(diào)用:

fun main(args: Array<String>) {

? ? Test.com()

}

class Test{

? ? companion object{

? ? ? ? fun com(){

? ? ? ? ? ? print("com")

? ? ? ? }

? ? }

}

頂級(jí)方法

在類外部定義的方法码秉,可理解為靜態(tài)方法逮矛,通過(guò)包名.方法名的方式調(diào)用:

package a.b.c

fun top(){

? ? print("top")

}

fun main(args: Array<String>) {

? ? a.b.c.top()

}

頂級(jí)方法與靜態(tài)方法區(qū)別

通過(guò)反編譯可以知道,其實(shí)他們不在一個(gè)類中转砖。

如果有頂級(jí)變量會(huì)新生成一個(gè)[文件名+kt]的類须鼎,頂級(jí)方法就在其中。

3.2 方法調(diào)用順序可變

通過(guò)指明參數(shù)名稱府蔗,可按任意順序傳參:

fun main(args: Array<String>) {

? ? log(name = "mao", age = 18)

}

fun log(age: Int, name: String) {

? ? print("age:$age,name:$name")

}

3.3 命名參數(shù)

方法樣例:

fun logInfo(

? ? ? ? name: String,

? ? ? ? age: Int,

? ? ? ? married: Boolean = true,

? ? ? ? language: String = "Chinese"

) {

? ? println("[name:$name,age:$age,married:$married,language:$language");

}

必須傳入未設(shè)置默認(rèn)值的參數(shù)

在上述logInfo方法中晋控,name和age未設(shè)置默認(rèn)值,必須傳參姓赤,其它參數(shù)可自由選擇:

? ? logInfo("ma",18)

? ? logInfo("ma",18,false);

? ? logInfo("ma",18,false,"English");

3.4 可變參數(shù)

使用vararg 修飾參數(shù):

fun logInfos(vararg infos: String) {

? ? for (info in infos) {

? ? ? ? print("$info? ")

? ? }

}

方法調(diào)用

logInfos("aaa","bbb","ccc")

四赡译,null安全(“?”,“?:”不铆,“!!”)

在Java開發(fā)中捶朵,null一直是個(gè)大問(wèn)題,哪怕我們?cè)傩⌒目衲校搽y免有疏忽的時(shí)候,Kotlin針對(duì)這個(gè)問(wèn)題做了一些措施品腹。

Kotlin將變量分為可以為Nullable類型 Non-Null類型岖食,變量在聲明時(shí)就要確定屬于哪個(gè)陣營(yíng)。

變量默認(rèn)Non-Null類型舞吭,如果想聲明Nullable的變量泡垃,需要用“?”修飾:

聲明Non-Null變量

? ? var a: String = "hello"?

聲明Nullable變量

? ? var b: String? = "world"

聲明變量時(shí)若直接賦值,變量類型由所賦值的類型決定

如在聲明b時(shí)羡鸥,將a賦值給b蔑穴,b的類型(Nullable或Non-Null)與a相同:

? ? var a: String? = "hello"? //Nullable

? ? var b = a? //與a相同,也是Nullable

4.1 Non-Null變量賦值

Non-Null變量不能賦值為null

var a: String=null //報(bào)錯(cuò):Null can not be a value of a non-null type String

Nullable變量無(wú)法直接賦值給Non-Null變量

? ? var a: String? = "hello"

? ? var b = "world"

? ? b = a //報(bào)錯(cuò):Type mismatch: inferred type is String? but String was expected

想要將Nullable變量賦值給Non-Null變量有以下方法:

先處理后賦值

? ? var a: String? = "hello"

? ? var b = "world"

? ? if (a != null) {

? ? ? ? b == a

? ? }

使用“!!”

? ? var a: String? = "hello"

? ? var b = "world"

? ? b = a!!

使用“!!”方法要注意惧浴,當(dāng)a為null時(shí)會(huì)拋出KotlinNullPointerException異常存和。

4.2 Nullable變量的使用

“?”符號(hào)的使用

Nullable變量進(jìn)行操作時(shí)要帶“?”,當(dāng)變量為null時(shí)衷旅,不會(huì)出現(xiàn)異常捐腿,而是返回結(jié)果null:

? ? var name: String? = null

? ? var len = name?.length

? ? print(len == null)? //輸出:true

“?:”符號(hào)的使用

這個(gè)符號(hào)的作用是當(dāng)它左邊的結(jié)果為null時(shí),進(jìn)行右邊的操作柿顶。

左邊結(jié)果不為null:

? ? var a: String? = "hello"

? ? var b = a?.length ?: 100? //很明顯左邊不為null

? ? println(b)? //輸出: 5

左邊結(jié)果為null:

? ? var a: String? = null

? ? var b = a?.length ?: 100? //左邊為null茄袖,返回右邊的100

? ? println(b)? //輸出: 100

五,類與繼承

5.1 類的創(chuàng)建

與Java一樣嘁锯,Kotlin也是用class關(guān)鍵字聲明類宪祥。

class User{}

Kotlin中一個(gè)類可以有一個(gè)主構(gòu)造方法(primary constructor)和一個(gè)或多個(gè)次構(gòu)造方法( secondary constructors)聂薪。

5.2 主構(gòu)造方法

主構(gòu)造方法通過(guò)在類名后面添加constructor和參數(shù)實(shí)現(xiàn):

class User private constructor(name: String) {}

如果沒(méi)有注解和可見的修飾符,constructor關(guān)鍵字可以省略:

class User(name: String) {}

初始化順序

類內(nèi)部的init模塊和變量的初始化順序按照他們出現(xiàn)的順序進(jìn)行

fun main(args: Array<String>) {

? ? User("mao")

}

class User(name: String) {

? ? val firstProperty = "First property".also(::println)

? ? init {

? ? ? ? println("First initializer")

? ? }

? ? val secondProperty = "Second property".also(::println)

? ? init {

? ? ? ? println("Second initializer")

? ? }

}

輸出:

First property

First initializer

Second property

Second initializer

成員變量和init模塊在初始化時(shí)可直接使用主構(gòu)造方法中的參數(shù)

class User(name: String) {

? ? var mName = name

? ? init {

? ? ? ? var mName = name

? ? }

}

5.3 次構(gòu)造方法

次構(gòu)造方法也使用constructor實(shí)現(xiàn)

class User {

? ? var name: String = ""

? ? constructor(name: String) {

? ? ? ? this.name = name

? ? }

}

當(dāng)類聲明了主構(gòu)造方法蝗羊,所有次構(gòu)造方法必須直接或間接調(diào)用主構(gòu)造方法

class User() {

? ? constructor(name: String) : this() {

? ? ? ? print("conconstructor")

? ? }

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

}

類中的變量初始化和init模塊初始化都是主構(gòu)造方法的一部分藏澳,所以都在次構(gòu)造方法之前執(zhí)行

fun main(args: Array<String>) {

? ? User("mao")

}

class User() {

? ? constructor(name: String) : this() {

? ? ? ? print("conconstructor")

? ? }


? ? var name = "property".also(::println)

? ? init{

? ? ? ? println("init")

? ? }

}

輸出:

property

init

conconstructor

當(dāng)一個(gè)類沒(méi)有任何構(gòu)造方法時(shí),默認(rèn)生成一個(gè)public類型的無(wú)參主構(gòu)造方法肘交,如果不希望這個(gè)默認(rèn)構(gòu)造方法存在笆载,可以主動(dòng)聲明一個(gè)主構(gòu)造方法

class User private constructor() {}

5.4 繼承

Kotlin中的類默認(rèn)是final類型的,想要被繼承涯呻,得用“open”關(guān)鍵字修飾凉驻。

open class Shape {}

class Rectangle : Shape {}

子類的所有構(gòu)造構(gòu)造方法必須直接或間接調(diào)用一個(gè)父類的構(gòu)造方法

open class Shape {

? ? constructor(name: String) {

? ? ? ? print(name)

? ? }

}

class Rectangle : Shape {

? ? constructor(name: String) : super(name) {}

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

}

方法重寫

繼承過(guò)程中,只有open修飾的方法才能被重寫复罐,重寫時(shí)要用override修飾涝登。

open特性也能被繼承,想要斷了open特性效诅,只需用final修飾即可胀滚。

open class Shape {

? ? open fun method() {}

}

open class Rectangle : Shape() {

? ? override fun method() {}

}

class Square : Rectangle() {

? ? final override fun method() {}

}

成員變量重寫

與方法重寫相同,只有open修飾的變量才能被重寫乱投,open同樣可以繼承咽笼,也可以用final中斷。

重寫過(guò)程中戚炫,變量可由val類型變?yōu)関ar類型剑刑,反之則不行。

open class Shape {

? ? open val name: String = "Shape"

}

open class Rectangle : Shape() {

? ? override var name: String = "Rectangle"

}

class Square : Rectangle() {

? ? final override var name = "Square"

}

調(diào)用父類方法和成員變量

可通過(guò)“super”關(guān)鍵字調(diào)用父類的方法和成員變量

open class Shape {

? ? open val name: String = "Shape"

? ? open fun draw() {}

}

open class Rectangle : Shape() {

? ? override var name: String = super.name

? ? override fun draw() {

? ? ? ? super.draw()

? ? }

}

內(nèi)部類調(diào)用外部類父類的方法

使用“super@Outer”方式:

open class Sup {

? ? open fun method() { println("Sup.method") }

}

class Sub:Sup(){

? ? inner class Inner{

? ? ? ? fun test(){

? ? ? ? ? ? super@Sub.method()

? ? ? ? }

? ? }

}

當(dāng)繼承的類和接口當(dāng)中出現(xiàn)相同的方法(方法名和參數(shù)都相同)双肤,通過(guò)類似泛型的方法明確調(diào)用哪個(gè)方法

interface Action {

? ? fun eat() {

? ? ? ? println("Action")

? ? }

}

open class Animal {

? ? open fun eat() {

? ? ? ? println("Animal")

? ? }

}

class Human() : Animal(), Action {

? ? override fun eat() {

? ? ? ? super<Action>.eat()

? ? ? ? super<Animal>.eat()

? ? }

}

六施掏,內(nèi)聯(lián)方法

使用高階方法會(huì)造成一些強(qiáng)制性的開銷:內(nèi)存分配和調(diào)用都是開銷。不過(guò)茅糜,在許多情況下有些開銷是可以避免的七芭。

例子:

現(xiàn)在把吃飯分為三個(gè)步驟:1.拿起筷子 2.吃 一口3.放下筷子

1.情況一:每吃一口都要放下筷子,即拿蔑赘、吃狸驳、放、拿米死、吃锌历、放...

操作順序:

1、2峦筒、3;? 1究西、2、3; 1物喷、2卤材、3...

這種情況不是飯菜不合胃口遮斥、就是吃撐了。

2.情況二:每吃若干口才放下筷子扇丛。

操作順序:

1术吗、2、2...2帆精、3; 1较屿、2、2...2卓练、3;... 1隘蝎、2、2...2襟企、3;

明顯這才是正確操作嘱么,能省不少力氣。

在JVM中也有類似的情況顽悼,JVM中每個(gè)線程都有一個(gè)虛擬機(jī)棧曼振,每個(gè)方法的從調(diào)用到完成,對(duì)應(yīng)著入棧蔚龙、出棧的過(guò)程冰评,如果將一方法分為多個(gè)方法時(shí),就會(huì)有更多的入棧木羹、出棧的開銷集索,使用內(nèi)聯(lián)方法能有效減少這部分開銷。

例一

現(xiàn)在我們要打印一個(gè)人汇跨,分三部分打印:頭妆距、身體和腳穷遂,其中身體構(gòu)造復(fù)雜,又要分三部分

1-1

fun main(args: Array<String>) {

? ? //printHead

? ? println("head")

? ? //printbody

? ? println("body1")

? ? println("body2")

? ? println("body3")

? ? //printFoot

? ? println("foot")

}

現(xiàn)在我們想把打印身體這部放到一個(gè)單獨(dú)的方法中娱据,使代碼更清晰:

1-2

fun main(args: Array<String>) {

? ? //printHead

? ? println("head")

? ? //printbody

? ? printBody()

? ? //printFoot

? ? println("foot")

}

fun printBody() {

? ? println("body1")

? ? println("body2")

? ? println("body3")

}

不過(guò)這樣一來(lái)多了部分開銷蚪黑,這時(shí)內(nèi)聯(lián)方法可以幫我們避免這部分開銷。

6.1 inline

定義內(nèi)聯(lián)方法需使用“inline”關(guān)鍵字修飾

1-3

fun main(args: Array<String>) {

? ? //printHead

? ? println("head")

? ? //printbody

? ? printBody()

? ? //printFoot

? ? println("foot")

}

inline fun printBody() {

? ? println("body1")

? ? println("body2")

? ? println("body3")

}

編譯過(guò)程會(huì)幫我們把1-3轉(zhuǎn)換為1-1的樣子中剩,提高了性能的同事又能保證結(jié)構(gòu)清晰忌穿。

例二

如何才能將代碼

lock(l) { foo() }

實(shí)現(xiàn)這樣的效果:

l.lock()

try {

? ? foo()

}

finally {

? ? l.unlock()

}

可以通過(guò)內(nèi)聯(lián)方法實(shí)現(xiàn):

inline fun <T> check(lock: Lock, body: () -> T): T {

? ? lock.lock()

? ? try {

? ? ? ? return body()

? ? } finally {

? ? ? ? lock.unlock()

? ? }

}

調(diào)用:

? ? var lock = ReentrantLock()

? ? check(lock) {print("hello")}

完整樣例:

開啟兩個(gè)線程,通過(guò)加同一把鎖實(shí)現(xiàn)同步:

fun main(args: Array<String>) {

? ? var lock = ReentrantLock()

? ? Thread() {

? ? ? ? check(lock) {

? ? ? ? ? ? for (i in 1..5) {

? ? ? ? ? ? ? ? TimeUnit.SECONDS.sleep(1)

? ? ? ? ? ? ? ? println("111")

? ? ? ? ? ? }

? ? ? ? }

? ? }.start()

? ? Thread() {

? ? ? ? check(lock) {

? ? ? ? ? ? for (i in 1..5) {

? ? ? ? ? ? ? ? TimeUnit.SECONDS.sleep(1)

? ? ? ? ? ? ? ? println("222")

? ? ? ? ? ? }

? ? ? ? }

? ? }.start()

}

inline fun <T> check(lock: Lock, body: () -> T): T {

? ? lock.lock()

? ? try {

? ? ? ? return body()

? ? } finally {

? ? ? ? lock.unlock()

? ? }

}

輸出:

111

111

111

111

111

222

222

222

222

222

6.2 noinline

inline 方法中的方法參數(shù)默認(rèn)是inline類型的结啼,如果想過(guò)濾掉inline特性掠剑,可用noinline修飾。

inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) {

? ? // ...

}

6.3 lambda與return

1.普通方法的lambda模塊中禁止使用return

fun foo() {

? ? ordinaryFunction {

? ? ? ? return // ERROR: can not make `foo` return here

? ? }

}

2.inline方法的lambda模塊可用使用return

fun foo() {

? ? inlineFunction {

? ? ? ? return // OK: the lambda is inlined

? ? }

}

fun hasZeros(ints: List<Int>): Boolean {

? ? ints.forEach {

? ? ? ? if (it == 0) return true // returns from hasZeros

? ? }

? ? return false

}

這么設(shè)計(jì)很好理解郊愧,普通方法的lambda模塊其實(shí)是另一個(gè)方法的方法體朴译,你在本方法中調(diào)用另一方法中的return算是怎么回事井佑,稍不注意容易理解錯(cuò)誤。

而inline方法可認(rèn)為是本方法的一部分眠寿,外面那層大括號(hào)包裝可當(dāng)它不存在躬翁,使用return就顯得和自然了。

3.那么想要在普通方法lambda return怎么辦盯拱?只需在return后加“@方法名”即可:

fun f() {

? ? test {

? ? ? ? return@test

? ? }

}

fun test(action: () -> Unit) {}

4.inline 類型的方法參數(shù)不能直接用于賦值盒发,要想用于賦值得用crossinline 修飾

inline fun f(crossinline body: () -> Unit) {

? ? val f = object: Runnable {

? ? ? ? override fun run() = body()

? ? }

}

6.4 reified

泛型只在編譯時(shí)起作用,在運(yùn)行時(shí)已經(jīng)被擦除狡逢,所以泛型標(biāo)記是沒(méi)法當(dāng)做對(duì)象使用的宁舰。

不過(guò)在Kotlin中,reified可修飾inline方法中的泛型甚侣,對(duì)其進(jìn)行反射操作明吩。

inline fun <reified T> membersOf() = T::class.members

fun main(s: Array<String>) {

? ? println(membersOf<User>())

}

class User() {

? ? var name = "mao"

}

七,enum殷费,data印荔,sealed,object

7.1 enum類

7.1.1與java不同详羡,Kotlin中enum當(dāng)做class的修飾符使用

1.常見用法:

enum class Direction {

? ? NORTH, SOUTH, WEST, EAST

}

2.有成員屬性時(shí):

enum class Color(val rgb: Int) {

? ? RED(0xFF0000),

? ? GREEN(0x00FF00),

? ? BLUE(0x0000FF)

}

打印成員的值

print(Color.RED.rgb)

3.匿名方法

假如有一個(gè)機(jī)器仍律,他有三種狀態(tài)start,run实柠,stop水泉,并且不斷循環(huán)我們可以這樣實(shí)現(xiàn):

enum class State {

? ? Start {

? ? ? ? override fun nextState() = Run

? ? },

? ? Run {

? ? ? ? override fun nextState() = Stop

? ? },

? ? Stop {

? ? ? ? override fun nextState() = Start

? ? };

? ? abstract fun nextState(): State

}

簡(jiǎn)單調(diào)用:

fun main(args: Array<String>) {

? ? var state = State.Stop

? ? println(state)

? ? for (i in 1..3) {

? ? ? ? state = state.nextState()

? ? ? ? println(state)

? ? }

}

輸出:

Stop

Start

Run

Stop

7.2 data類

在Java中,我們?cè)谛畔㈩悇?chuàng)建信息類時(shí)總是伴隨大量getter/setter方法窒盐,雖然可以用工具自動(dòng)生成草则,但也影響美觀。Kotlin中使用data類型幫助我們解決了這個(gè)問(wèn)題蟹漓。

data class User(

? ? ? ? var name: String,

? ? ? ? var age: Int

)

在編譯時(shí)炕横,根據(jù)主構(gòu)造器中的參數(shù)會(huì)自動(dòng)生成getter/setter,hashcode()葡粒,toString()份殿,equals(),copy()等方法嗽交。

我們無(wú)法直接調(diào)用getter/setter 方法卿嘲,但我們對(duì)它的操作上本質(zhì)上都是通過(guò)調(diào)用getter/setter方法實(shí)現(xiàn)的。

data class User(

? ? ? ? var name: String,

? ? ? ? var age: Int

)

設(shè)置和修改變量的值

? ? var user = User("mao", 18)

? ? user.name = "zhang"

? ? user.age = 3

copy()

當(dāng)我們想將對(duì)象復(fù)制一份時(shí),可用copy()方法:

? ? var user = User("mao", 18)

? ? var user2 = user.copy()

如果想改變某個(gè)變量:

? ? var user=User("mao",18)

? ? var user2=user.copy(age=100)

7.3 sealed類

sealed類可看做時(shí)enum類的一種拓展夫壁,相比于enum的常量以單一實(shí)例存在拾枣,sealed類的子類可以有多種確定的類型。

sealed類自身是抽象類盒让,它的子類不能是抽象類放前,子類和它必須在同一個(gè)文件中忿磅。

sealed class Expr

data class Const(val number: Double) : Expr()

data class Sum(val e1: Expr, val e2: Expr) : Expr()

object NotANumber : Expr()

與when搭配使用非常方便

fun eval(expr: Expr): Double = when(expr) {

? ? is Const -> expr.number

? ? is Sum -> eval(expr.e1) + eval(expr.e2)

? ? NotANumber -> Double.NaN

}

栗子:現(xiàn)在將人根據(jù)顏值和智商分為4類:

1.有顏值有智商

2.有顏值沒(méi)智商

3.沒(méi)智商有顏值

4.沒(méi)智商沒(méi)顏值

sealed class Feature

data class IQ(var score: Int) : Feature()

data class FaceScore(var Sore: Int) : Feature()

data class Sum(var f1: Feature, var f2: Feature) : Feature()

object None : Feature()

然后設(shè)計(jì)評(píng)分機(jī)制:顏值(faceScore)和智商(IQ)基礎(chǔ)分0-10分

sum(總分)= faceScore * 8 + IQ * 2

計(jì)算方法實(shí)現(xiàn):

fun cal(f: Feature): Int = when (f) {

? ? is IQ -> {

? ? ? ? f.score * 2

? ? }

? ? is FaceScore -> f.Sore * 8

? ? is Sum -> cal(f.f1) + cal(f.f2)

? ? None -> 0

}

計(jì)算:

fun main(args: Array<String>) {

? ? var f1 = IQ(10)

? ? var f2 = FaceScore(10)

? ? var f3 = Sum(f1, f2)

? ? var f4=None

? ? cal(f1).also(::println)

? ? cal(f2).also(::println)

? ? cal(f3).also(::println)

? ? cal(f4).also(::println)

}

7.4 object

object到底有什么作用呢?先創(chuàng)建一個(gè)最簡(jiǎn)單的object類

用來(lái)實(shí)現(xiàn)單例模式

object O

反編譯獲得Java代碼:

public final class O {

? ? public static final O INSTANCE;

? ? static {

? ? ? ? O localo = new O();

? ? ? ? INSTANCE = localo;

? ? }

}

這是一種單例模式的實(shí)現(xiàn)方法凭语,如此看來(lái)object 可以用來(lái)實(shí)現(xiàn)單例模式葱她。

簡(jiǎn)單用法:

fun main(args: Array<String>) {

? ? O.test()

? ? O.name = "hello"

}

object O {

? ? var name = "mao"

? ? fun test() {

? ? ? ? print("test")

? ? }

}

object還可以用來(lái)實(shí)現(xiàn)匿名內(nèi)部類

fun main(args: Array<String>) {

? ? var btn = Btn()

? ? btn.onClickLsn = object : Btn.OnClickLsn {

? ? ? ? override fun click() {

? ? ? ? ? ? print("click")

? ? ? ? }

? ? }

? ? btn.callClick()

}

class Btn() {

? ? var onClickLsn: OnClickLsn? = null

? ? fun callClick() {

? ? ? ? onClickLsn?.click()

? ? }

? ? interface OnClickLsn {

? ? ? ? fun click()

? ? }

}

object類可以繼承一個(gè)類和多個(gè)接口。

當(dāng)父類有構(gòu)造方法時(shí)似扔,應(yīng)傳入對(duì)應(yīng)的參數(shù)吨些。

interface A

open class B(age: Int) {

? ? var mAge = age

}

var c: A = object : B(18), A {}

object可以做private方法和成員變量的返回值,不能做public方法和成員變量的返回值炒辉。

作為private方法和變量的返回值時(shí)豪墅,返回類型是匿名對(duì)象類型,可以訪問(wèn)內(nèi)部成員黔寇。

而最為public方法和變量返回值時(shí)偶器,返回類型為Any,不能訪問(wèn)內(nèi)部成員缝裤。

class C {

? ? // private方法屏轰,返回類型是匿名對(duì)象類型

? ? private fun foo() = object {

? ? ? ? val x: String = "x"

? ? }

? ? // public方法,返回類型是 Any

? ? fun publicFoo() = object {

? ? ? ? val x: String = "x"

? ? }

? ? fun bar() {

? ? ? ? val x1 = foo().x? ? ? ? // 沒(méi)問(wèn)題

? ? ? ? val x2 = publicFoo().x? //報(bào)錯(cuò)憋飞,無(wú)法引用x

? ? }

}

object和companion

在類內(nèi)部霎苗,object和companion可實(shí)現(xiàn)靜態(tài)成員和靜態(tài)方法的效果。

class Outer {

? ? companion object Inner {

? ? ? ? var property = "property"

? ? ? ? fun method() {

? ? ? ? ? ? print("method")

? ? ? ? }

? ? }

}

調(diào)用:

Outer.Inner.property.also(::println)

Outer.Inner.method()

通過(guò)反編譯可知此處的 Inner并不是類榛做,而是一個(gè)靜態(tài)常量實(shí)例唁盏。

正常使用過(guò)程中Inner可以省去:

Outer.property.also(::println)

Outer.method()

這兩種方法本質(zhì)上是一樣的。

類中的companion object修飾的Inner也可去掉:

class Outer {

? ? companion object {

? ? ? ? var property = "property"

? ? ? ? fun method() {

? ? ? ? ? ? print("method")

? ? ? ? }

? ? }

}

當(dāng)沒(méi)有Inner的情況下检眯,Inner默認(rèn)為Companion

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末厘擂,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子锰瘸,更是在濱河造成了極大的恐慌驴党,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件获茬,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡倔既,警方通過(guò)查閱死者的電腦和手機(jī)恕曲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)渤涌,“玉大人佩谣,你說(shuō)我怎么就攤上這事∈蹬睿” “怎么了茸俭?”我有些...
    開封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵吊履,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我调鬓,道長(zhǎng)艇炎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任腾窝,我火速辦了婚禮缀踪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘虹脯。我一直安慰自己驴娃,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開白布循集。 她就那樣靜靜地躺著唇敞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪咒彤。 梳的紋絲不亂的頭發(fā)上疆柔,一...
    開封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音蔼紧,去河邊找鬼婆硬。 笑死,一個(gè)胖子當(dāng)著我的面吹牛奸例,可吹牛的內(nèi)容都是我干的彬犯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼查吊,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼谐区!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起逻卖,我...
    開封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤宋列,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后评也,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炼杖,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年盗迟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了坤邪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡罚缕,死狀恐怖艇纺,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤黔衡,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布蚓聘,位于F島的核電站,受9級(jí)特大地震影響盟劫,放射性物質(zhì)發(fā)生泄漏夜牡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一捞高、第九天 我趴在偏房一處隱蔽的房頂上張望氯材。 院中可真熱鬧,春花似錦硝岗、人聲如沸氢哮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)冗尤。三九已至,卻和暖如春胀溺,著一層夾襖步出監(jiān)牢的瞬間裂七,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工仓坞, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留背零,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓无埃,卻偏偏與公主長(zhǎng)得像徙瓶,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子嫉称,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

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