kotlin優(yōu)勢
簡潔(data class
自帶get set equals hashCode toString copy
)
安全(區(qū)別可空和不可空婉刀,避免各類空指針)
與java互操作性
基礎(chǔ)特性及用法
顯式
Kotlin 通過細(xì)致的顯式聲明和約束,使代碼的邏輯和結(jié)構(gòu)都更加清晰棒动,并減少錯(cuò)誤。
可變與只讀
var 聲明可變變量
val 聲明制度變量
只讀集合List<T> Set<T> Map<K, V>
可變集合MutableList<T> MutableSet<T> MutableMap<K, V>
類型安全
是靜態(tài)隱式類型的,提供類型推斷-
空引用安全
區(qū)分可空與非空
可空安全調(diào)用bob?.department?.head?.name
Elvis
操作符:如果?:
左邊的表達(dá)式不為null
,則返回該表達(dá)式;否則返回null
。val l = b?.length ?: ‐1 // 等效于 val l: Int = if (b != null) b.length else ‐1
集合
集合也可以根據(jù)是否允許容納 null
進(jìn)行區(qū)分(List<Int?> / List<Int>
)。
可以使用 filterNotNull()
從可為空類型的集合中過濾非空類型。
- 修飾符
可見性
peivate
: 類內(nèi)可見
protected
:private
+子類可見
internal
:同一模塊(一起編譯的一組kotlin源文件)
public
:可見
open
:用于繼承的類要顯示的聲明open
inner
:內(nèi)部類碴开,默認(rèn)內(nèi)部類不能訪問外部類
使用inner
標(biāo)記的類會攜帶外部類引用巴碗,可以訪問外部類成員
- 工程化
數(shù)據(jù)類解構(gòu)聲明
data class User(val name: String = "", val age: Int = 0)
數(shù)據(jù)類自動(dòng)對每個(gè)屬性生成對應(yīng)的componentN()
函數(shù)明垢,用于解構(gòu)聲明: val jane = User("Jane", 35)
val (name, age) = jane
- 擴(kuò)展函數(shù)
kotlin提供了對類擴(kuò)展的功能溯革,可以在不繼承或使用設(shè)計(jì)模式(如裝飾著)的情況下抖单,為類添加新的功能
如
下面為MutableList<Int>
添加了一個(gè)swap()
函數(shù):
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // this 對應(yīng)列表本身 this[index1] = this[index2]
this[index2] = tmp
}
同時(shí)聲明以下兩個(gè)函數(shù)回導(dǎo)致沖突:
fun String?.toDecimal(): BigDecimal {...}
fun toDecimal(value: String?): BigDecimal {...}
擴(kuò)展函數(shù)是靜態(tài)解析的货矮,其調(diào)用取決于聲明類型而不是運(yùn)行時(shí)的實(shí)際類型
open class C class D: C()
fun C.foo() = "c" fun D.foo() = "d"
fun printFoo(c: C) {
println(c.foo())
}
printFoo(D()) // c
- 操作符重載
// todo
進(jìn)階特性及用法
kotlin中函數(shù)的地位
kotlin中函數(shù)是頭等的燃少≌颍可以存儲在變量與數(shù)據(jù)結(jié)構(gòu)中矮烹、可以作為參數(shù)傳遞給高階函數(shù)仁期、也可以從高階函數(shù)返回。
高階函數(shù)
高階函數(shù)是將函數(shù)用作參數(shù)或返回值的函數(shù)理逊。
fun <T> lock(lock: Lock, body: () ‐> T): T {
lock.lock()
try {
return body()
}
finally {
lock.unlock()
}
}
上述代碼中道媚,body
就是函數(shù)類型,該函數(shù)沒有輸入?yún)?shù),輸出類型為T舀奶。 它被 lock
保護(hù)腹躁,在 try
塊中調(diào)用,其結(jié)果由 lock()
函數(shù)返回。 lock()
的調(diào)用方法如下套硼。
fun toBeSynchronized() = sharedResource.operation()
val result1 = lock(myLock, ::toBeSynchronized)
val result2 = lock(myLock, { sharedResource.operation() })
函數(shù)類型
Kotlin 使用類似 (Int) -> String
的一系列函數(shù)類型來處理函數(shù)的聲明: val onClick: () -> Unit = ……策菜。
這些類型具有與函數(shù)簽名對應(yīng)的特殊標(biāo)示法,即他們的參數(shù)和返回值:
參數(shù)放在
()
中棵癣,參數(shù)放在->
左側(cè),返回值放在->
右側(cè)。參數(shù)列表可為空敞临,返回類型不可省略攀涵,如:()->Unit
參數(shù)類型可以有一個(gè)額外的接收者類型踪区,他的表示法如:
A.(B) -> C
。它表示溪椎,參數(shù)A對象中有一個(gè)方法接收一個(gè)B類型的參數(shù),并返回C。掛起函數(shù) 一種特殊類型的函數(shù)椿浓,有一個(gè)suspend修飾符荠瘪。如
suspend ()->Unit
函數(shù)類型指定為可空需使用圓括號
((Int,Int) -> Int)?
函數(shù)類型可以使用圓括號結(jié)合
(Int -> (Int -> Int))
箭頭表示法是右結(jié)合的
(Int -> (Int -> Unit))
等價(jià)于(Int -> Int -> Unit)
但不等價(jià)于((Int -> Int) -> Unit)
還可以通過類型別名給函數(shù)類型起一個(gè)別稱:
typealias ClickHandler = (Button,ClickEvent) ->Unit
函數(shù)類型實(shí)例調(diào)用
函數(shù)類型的值可以通過invoke(...)
調(diào)用:f.invoke(x)
或者直接f(x)
。
如果該值具有接收者類型臀突,那么應(yīng)該將接收者作為第一個(gè)參數(shù)傳遞。調(diào)用接收者的函數(shù)值類型的另一個(gè)方式是在其前面加上接收者對象啊胶,就好比該值是一個(gè)擴(kuò)展函數(shù):
例
val stringPlus: (String, String) -> String = String::plus
val intPlus: Int.(Int) -> Int = Int::plus
println(stringPlus.invoke("<-", "->"))
println(stringPlus("Hello, ", "world!"))
println(intPlus.invoke(1, 1))
println(intPlus(1, 2))
println(2.intPlus(3)) // 類擴(kuò)展調(diào)用
- Lamda表達(dá)式
沒有經(jīng)過聲明而直接作為表達(dá)式傳遞的函數(shù)诫尽。
編寫規(guī)則:
1.Lamda表達(dá)式要被{}
包圍
2.參數(shù)(參數(shù)類型可以省略)在->
左邊
3.函數(shù)體在->
右邊
4.最后一個(gè)表達(dá)式作為函數(shù)的返回值
val sum1: (Int, Int) ‐> Int = { x, y ‐> x + y }
val sum2 = { x: Int, y: Int ‐> x + y }
sum1(1, 2)
sum2(1, 2)
如果函數(shù)的最后一個(gè)參數(shù)為表達(dá)式,且你傳遞的是Lamda表達(dá)式炬守,那么kotlin的慣例是把Lamda表達(dá)式寫在括號外面:
lock(myLock, { sharedResource.operation() })
// 等效于
lock (myLock) {
sharedResource.operation()
}
- 閉包
Lambda表達(dá)式和匿名函數(shù)可以訪問其閉包牧嫉,也就是外部作用域聲明的變量。不同的是减途,java可以修改從閉包中獲取的變量驹止。
-
匿名函數(shù)
val isOdd: (Int) -> Boolean = fun(value: Int): Boolean { return value % 2 == 1 } val isOdd = fun(value: Int): Boolean { return value % 2 == 1 } val isOdd = fun(value: Int): Boolean = value % 2 == 1
使用
val values = listOf(1, 2, 3, 4, 5)
val filtered = filter(values, isOdd)
-
可調(diào)用引用
1.引用頂層、局部观蜗、成員臊恋、擴(kuò)展函數(shù):::isOdd String::toInt
2.引用頂層、成員墓捻、擴(kuò)展屬性:List<Int>::size
3.引用構(gòu)造器:::Regex
例val filtered = filter(values, ::isOdd)
-
實(shí)現(xiàn)了函數(shù)類型接口的類抖仅。使用方法同普通類一樣,定義方法如下:
class OddChecker: (Int) -> Boolean { override fun invoke(value: Int): Boolean { return value % 2 == 1 } }
Lambda語法糖
如果Lambda只有一個(gè)參數(shù)砖第,且編譯器可以推斷其類型撤卢,則可以省略參數(shù),此時(shí)該參數(shù)會被隱式的聲明為it
val isOdd: (Int) -> Boolean = { value -> value % 2 == 1 } val isOdd: (Int) -> Boolean = { it % 2 == 1 }
val isOdd: (Int) -> Boolean = { it % 2 == 1 }
如果省略回影響編譯器的類型推斷梧兼,則不可省略
val isOdd = { value: Int -> value % 2 == 1 }
如果函數(shù)只返回一個(gè)表達(dá)式放吩,則可以省略函數(shù)體的大括號(和返回類型),使用 = 連接函數(shù)體
fun double(x: Int): Int { return x * 2 }
fun double(x: Int): Int = x * 2
fun double(x: Int) = x * 2
- 純函數(shù)
1.對于同樣的輸入羽杰,返回同樣的輸出
2.調(diào)用函數(shù)不會產(chǎn)生副作用(不會修改程序狀態(tài))
-
內(nèi)置函數(shù)
let
T
的擴(kuò)展函數(shù)渡紫,將block
中的操作應(yīng)用在T
上,并返回block
的值考赛,在block
中使用it
引用T
的實(shí)例
also
T
的擴(kuò)展函數(shù)惕澎,將block
中的操作應(yīng)用在T
上,并返回T
颜骤,在block
中使用it
引用T
的實(shí)例
with
將block
中的操作應(yīng)用在T
上唧喉,并返回block
的值,不是擴(kuò)展函數(shù),block
中使用this
引用T
的實(shí)例
run
T
的擴(kuò)展函數(shù)八孝,將block
中的操作應(yīng)用在T
上董朝,并返回block
的值,在block
中使用this
引用T的實(shí)例
apply
T
的擴(kuò)展函數(shù)干跛,將block
中的操作應(yīng)用在T
上益涧,并返回T
,在block
中使用this
引用T
的實(shí)例
-
集合的操作
var records: List<FoodRecord> = ... val models: List<FoodModel> = records // List<FoodRecord> .map { it.transform() } // List<FoodModel> .filter { it.units.isNotEmpty() } // List<FoodModel> .map { food -> food.units.map { unit -> food.deepCopy(listOf(unit)) } } //List<List<FoodModel>> .flatten() // List<FoodModel> val foodMap: Map<String, List<FoodModel>> = models.groupBy { it.foodCategoryName }
-
參數(shù)默認(rèn)值和命名參數(shù)
fun reformat(str: String,normalizeCase: Boolean = true,upperCaseFirstLetter: Boolean = true, divideByCamelHumps: Boolean = false, wordSeparator: Char = ' ') { ... }
默認(rèn)參數(shù)調(diào)用
reformat(str)
指定參數(shù)調(diào)用
reformat(str, true, true, false, '_')
使用命名參數(shù)驯鳖,提高代碼可讀性
reformat(str,normalizeCase = true,upperCaseFirstLetter = true,divideByCamelHumps = false,wordSeparator = '_')
- 委托屬性
使用如下形式聲明委托屬性
class Example {
var p: String by Delegate()
}
實(shí)現(xiàn)委托
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.")
}
}
使用
val e = Example()
e.p = "HelloWorld" // HelloWorld has been assigned to 'p' in Example@17f052a3. println(e.p) // Example@17f052a3, thank you for delegating 'p' to me!
-
標(biāo)準(zhǔn)委托 lazy
val lazyValue: String by lazy { println("computed!") "Hello" } fun main(args: Array<String>) { println("Running in main") println(lazyValue) println(lazyValue) }
輸出
Running in main
computed!
Hello
Hello
-
observable
Delegates.observable接收兩個(gè)參數(shù):初始值和屬性變化時(shí)的Handlerclass User { var name: String by Delegates.observable("<no name>") { prop, old, new -> println("$old -> $new") } } fun main(args: Array<String>) { val user = User() user.name = "first" user.name = "second" }
輸出
<no name> -> first
first -> second
-
map
class User(val map: MutableMap<String, Any?>) { var name: String by map var age: Int by map } val user = User(mapOf( "name" to "John Doe","age" to 25 )) println(user.name) // John Doe println(user.age) // 25 user.name = "Mary Sue" user.age = 26 println(user.map) // {name=Mary Sue, age=26}
內(nèi)聯(lián)函數(shù)和具體化類型參數(shù)
使用高階函數(shù)會創(chuàng)建額外的函數(shù)對象并捕獲閉包,帶來額外開銷久免。通過inline標(biāo)注為內(nèi)聯(lián)函數(shù)浅辙,可以避免這些開銷。
inline
會同時(shí)對函數(shù)本身和作為函數(shù)參數(shù)的lambda表達(dá)式生效
inline fun <T> lock(lock: Lock, body: () -> T): T { lock.lock()
try {
return body()
} finally {
lock.unlock()
}
}
例
foo()
lock(l) { foo() }
bar()
相當(dāng)于
foo()
l.lock()
try {
foo()
} finally {
l.unlock()
}
bar()
在內(nèi)聯(lián)函數(shù)中使用 reified
將范型標(biāo)記為具體化類型參數(shù)阎姥,在內(nèi)聯(lián)函數(shù)中记舆,可以像訪問參數(shù)一樣訪問具體化類型參數(shù)。
inline fun <reified T: Any> fromJson(json: String): T =
gson.fromJson(json, T::class.java)
val user = fromJson<User>(json)
val user: User = fromJson(json)
- 支持字段
在類或累的主構(gòu)造器中使用var
或val
聲明類的屬性呼巴,不能再類中聲明Filed
泽腮,Kotlin會自動(dòng)在需要的時(shí)候?yàn)閷傩蕴砑右粋€(gè)支持字段。
class Person {
var age: Int = 10
get() {
return field
}
set(value) {
field = value
}
}
-
具有支持字段的屬性
如果屬性使用了至少一個(gè)默認(rèn)的訪問器
(get/set)
衣赶,或者自定義訪問器中使用了filed
標(biāo)識符訪問了支持字段诊赊,則會生成對應(yīng)的支持字段。class Person( val name: String, var age: Int){ val isAdult: Boolean = age >= 18
}
這里的isAdult
相當(dāng)于java的public final boolean isAdult = age >= 18
val person = Person("John", 17)
println(person.isAdult) \ false p
erson.age++
println(person.isAdult) \ false -
沒有支持字段的屬性
class Person( val name: String, var age: Int ){ val isAdult: Boolean get() { return age >= 18 } // 等同于get() = age >= 18 }
這里的isAdult
相當(dāng)于java的
public boolean isAdult() {
return age >= 18;
}
val person = Person("John", 17)
println(person.isAdult) \\ false
person.age++
println(person.isAdult) \\ true