Android Kotlin第二篇,接下來讓我們一起認(rèn)識下kotlin里的函數(shù)和Kotlin新特性之一Lambda表達(dá)式。當(dāng)然你深入了解Kotlin的時(shí)候局装,你會發(fā)覺Kotlin函數(shù)比java函數(shù)真的強(qiáng)大很多,能節(jié)省很多,也會讓自己代碼清晰很多弹灭。
Android Kotlin系列前面會先講解Kotlin基礎(chǔ),后續(xù)才會有具體到Android內(nèi)揪垄,目前是用Android做調(diào)試測試驗(yàn)證Kotlin
一穷吮、函數(shù)
1、函數(shù)聲明
函數(shù)聲明可以不帶返回饥努,也可以帶返回捡鱼,例如:
//帶返回的聲明
fun test0(a0 : Int) : Int{
return a0
}
//不帶返回的聲明,tes1函數(shù)等同于test2
fun test1(a1 : Int):Unit{
}
//不帶返回的聲明
fun test2(a2 : Int){
}
與java區(qū)別:
a酷愧、java聲明驾诈,默認(rèn)是私有的函數(shù);Kotlin默認(rèn)是公有的函數(shù)
b溶浴、Kotlin函數(shù)必須有fun關(guān)鍵字
c乍迄、java返回類型在函數(shù)名前;Kotlin返回類型在方法()后后面用“:”隔開士败,如上test0函數(shù)
2闯两、函數(shù)調(diào)用
可以直接調(diào)用,例如:
FunGoKotlin().test0(1)
3、函數(shù)的參數(shù)
函數(shù)聲明時(shí)參數(shù)漾狼,可以不帶默認(rèn)值參數(shù)重慢,也可以帶默認(rèn)值參數(shù),這里與java區(qū)別比較大逊躁,我覺得這點(diǎn)比java更加強(qiáng)大和靈活伤锚,例如:
//默認(rèn)參數(shù), b3 : Int = 10
fun test3(a3 : Int, b3 : Int = 10, c3 : Int = 10,d3 : String = "默認(rèn)值") : Int{
return a3+b3
}
//調(diào)用默認(rèn)參數(shù)
val a3_1 : Int = test3(1)//使用默認(rèn)參數(shù)值
val a3_2 = test3(1,2);//不使用默認(rèn)參數(shù)值
val a3_3 = test3(1,c3 = 3, d3 = "")//假設(shè)我不想穿其中某個(gè)參數(shù)(這個(gè)參數(shù)最后一位d3),后面回去前面在賦值的時(shí)候可以使用參數(shù)“參數(shù)名稱=賦值”進(jìn)行調(diào)用函數(shù)
這里聲明函數(shù)的時(shí)候志衣,函數(shù)參數(shù)可以帶默認(rèn)值“d3 : String = "默認(rèn)值"”屯援,調(diào)用的時(shí)候也可以使用“函數(shù)名(d3=“賦值”)”指定某個(gè)參數(shù)賦值
注意:
//繼承復(fù)寫方法,override 關(guān)鍵字函數(shù)參數(shù)是不能有默認(rèn)值的
open class A {
open fun foo(i: Int = 10) { }
}
class B : A() {
override fun foo(i: Int) { } // 不能有默認(rèn)值
}
與java區(qū)別:
與java最大區(qū)別就是念脯,Kotlin是可以對參數(shù)重命名狞洋,并且可以調(diào)用參數(shù)重命名進(jìn)行賦值,而且函數(shù)的參數(shù)是可以帶有默認(rèn)值的
4绿店、單表達(dá)式函數(shù)
當(dāng)函數(shù)返回單個(gè)表達(dá)式時(shí)吉懊,可以省略花括號并且在 = 符號之后指定代碼體即可,例如;
//單表達(dá)式函數(shù)
fun test4(x : String) = "單表達(dá)式函數(shù)" + x
fun test4(x : Int,y : Int) = x + y
這點(diǎn)比java強(qiáng)大
5假勿、中綴標(biāo)示方法(全新特性)
這個(gè)是java所沒有的特性
函數(shù)還可以用中綴表示法調(diào)用借嗽,當(dāng)
- 他們是成員函數(shù)或擴(kuò)展函數(shù)
- 他們只有一個(gè)參數(shù)
- 他們用 infix關(guān)鍵字標(biāo)注
讓我們看個(gè)具體例子,這樣會更加好懂一些转培。例如:
//中綴標(biāo)示方法恶导,必須要帶參數(shù)才行,而且如果只有一個(gè)參數(shù)該參數(shù)是不能設(shè)置默認(rèn)值浸须,關(guān)鍵字:infix
infix fun Int.test5_1(x : Int) = x+1
infix fun Int.test5_2(x : Int) : Int{ return x * 2 }
infix fun String.test5_2(x : Int) : Int{ return x * 2 }
fun test5(x : Int) : Int{
when(x){
//中綴調(diào)用如下惨寿,總結(jié)是有兩種:“xx 方法 傳值”,“xx.方法(傳值)”
//這里提前用到了when控制流
//也提前用到了retrun返回删窒,如果在多個(gè)控制流里裂垦,具體如果返回不加“@”,默認(rèn)是返回最近的一層控制流肌索,“@”可以指定返回到某層
0 -> return@test5 1 test5_2 4
1 -> return@test5 2 test5_2 4
2 -> return@test5 1.test5_2(4)
3 -> return@test5 "" test5_2 6
4 -> return@test5 "".test5_2(6)
else -> return@test5 0
}
}
在這里你可以更加自己的需求蕉拢,擴(kuò)展一些自己常用的方法
6、可變數(shù)量的參數(shù)(vararg)
參數(shù)前加上vararg诚亚,參數(shù)數(shù)量就可以是變化的晕换。因?yàn)樯厦嫠f的參數(shù)特性,參數(shù)是可以重新命名且指定賦值亡电,所以使得其可變數(shù)量的參數(shù)比java更加強(qiáng)大届巩,那么強(qiáng)大在何處呢硅瞧,如下:
java這樣聲明是不可以的
public void test0(String a,String... b,String c){
}
但是Kotlin這樣聲明完全可以
//可變數(shù)量的參數(shù),關(guān)鍵字varargs
//一個(gè)函數(shù)里只能有一個(gè)vararg
fun test6(a : String, vararg b : String, c : String) : String{return ""}
//調(diào)用
val log : String = test6(topFun(),"a0","a1",c = "ad")
val log1 : String = test6(topFun(),"a0","a1",c = "ad")
7份乒、函數(shù)的作用域
在 Kotlin 中函數(shù)可以在文件頂層聲明,這意味著你不需要像一些語言如 Java、C# 或 Scala 那樣創(chuàng)建一個(gè)類來保存一個(gè)函數(shù)或辖。此外除了頂層函數(shù)瘾英,Kotlin 中函數(shù)也可以聲明在局部作用域、作為成員函數(shù)以及擴(kuò)展函數(shù)颂暇。
函數(shù)可以在文件頂層聲明,也就是說在文件里函數(shù)不一定要放在類里缺谴,可以放在外面和package一級,例如:
package com.xiaoqiang.kotlin.base
//在 Kotlin 中函數(shù)可以在文件頂層聲明耳鸯,這意味著你不需要像一些語言如 Java湿蛔、C# 或 Scala 那樣創(chuàng)建一個(gè)類來保存一個(gè)函數(shù)
fun topFun() : String{
return "aa"
}
class FunGoKotlin {
//調(diào)用頂層函數(shù)
val test7 = topFun()
}
局部函數(shù)(新特性)
//Kotlin 支持局部函數(shù),即一個(gè)函數(shù)在另一個(gè)函數(shù)內(nèi)部
fun test6() : String{
fun test66() : String{
return "test66()"
}
val log : String = test6("a", "b0","b1",c = "ad")
return log+","+test66()
}
我感覺這個(gè)還是有點(diǎn)用處县爬,可以是代碼邏輯更加清晰阳啥,增加代碼可讀性
8、支持泛型函數(shù)
這個(gè)和java差不多财喳,唯一區(qū)別就是要在前面尖括號里指定泛類型
//函數(shù)可以有泛型參數(shù)察迟,通過在函數(shù)名前使用尖括號指定。
fun <T> test8(t : T) : T{
return t
}
fun <L> test8(l : List<L>) : List<L> {
return l
}
9耳高、支持尾遞歸函數(shù)
//kotlin是支持尾遞歸函數(shù)的扎瓶,這可以允許一些算法可以正常的使用循環(huán)而不是寫一個(gè)遞歸函數(shù),而且沒有內(nèi)存溢出的風(fēng)險(xiǎn)泌枪。
// 如果一個(gè)函數(shù)用tailrec修飾符標(biāo)記就滿足了編譯器優(yōu)化遞歸的條件概荷,并用高效迅速的循環(huán)代替它。
//當(dāng)函數(shù)被關(guān)鍵字tailrec修飾碌燕,同時(shí)滿足尾遞歸(tail recursion)的函數(shù)式編程方式的形式時(shí)乍赫,編譯器就會對代碼進(jìn)行優(yōu)化,
// 消除函數(shù)的遞歸調(diào)用, 產(chǎn)生一段基于循環(huán)實(shí)現(xiàn)的, 快速而且高效的代碼。
tailrec fun findFixPoint(x: Double = 1.0): Double
= if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))
二陆蟆、Lambda表達(dá)式與高階函數(shù)
當(dāng)你學(xué)習(xí)了Lambda表達(dá)式后雷厂,你會發(fā)現(xiàn)Lambda表達(dá)式真的是一種非常簡單的方法,去定義一個(gè)匿名函數(shù)叠殷,比java能節(jié)省很多代碼,當(dāng)然越簡單也許理解起來越困難像棘,但是當(dāng)你理解了后,你會發(fā)現(xiàn)真的很好用壶冒,它能避免我們?nèi)懸恍┌四承┖瘮?shù)的抽象類火災(zāi)接口缕题,然后在類中去實(shí)現(xiàn)它們。在Kotlin瘪松,我們把一個(gè)函數(shù)作為另外一個(gè)函數(shù)的參數(shù)墅诡。
1壳嚎、lambda表達(dá)式
Lambda表達(dá)式的完整語法形式,例如下:
val sum = { x: Int, y: Int -> x + y }
lambda表達(dá)式總是被大括號擴(kuò)著末早,參數(shù)聲明在大括號內(nèi),函數(shù)體在 ->符號之后然磷。
其實(shí)這個(gè)調(diào)用起來更像是函數(shù)焙糟,直接sum(x,y),所有Kotlin允許我們這樣來寫Lambda表達(dá)式:
val sum1 : (Int, Int) -> Int = { x, y -> x + y }
這樣看起來和函數(shù)很像
那么對應(yīng)的返回值呢样屠?有是什么穿撮。如果返回值類型是Unit,可以
val sum2 : (Int,Int) -> Unit = {x,y -> Unit}
如果返回值類型確定不是Unit痪欲,默認(rèn)返回值是 -> 后函數(shù)體中最后一個(gè)表達(dá)式視為返回值悦穿,例如:
val sum3 : (Int, Int) -> Int = { x,y ->
val temp = x + y
temp * 2//這個(gè)視為最后返回值
}
單個(gè)參數(shù)的隱式名稱(it)
當(dāng)然Kotlin還給我們提供了特殊約定,特殊情況下還可以進(jìn)一步減少代碼业踢,如果Lambda表達(dá)式只有一個(gè)參數(shù)值時(shí)栗柒,我們可以使用it來代替單個(gè)參數(shù),視為隱式名稱知举,例如;
val sum4 : (Int) -> Int = { it * 10 }
看到這里瞬沦,我想你會發(fā)現(xiàn)Lambda表達(dá)式基本上沒有指明返回類型,而是自動(dòng)推斷出類型雇锡。如果你想指明返回類型的話逛钻,你可以使用匿名函數(shù)
匿名函數(shù)
匿名函數(shù),沒有名字锰提,其他語法和常規(guī)函數(shù)類似曙痘。舉個(gè)具體實(shí)例吧:
//匿名函數(shù)
val test13 = fun(x : Int, y : Int):Int { return x + y }
//調(diào)用匿名函數(shù)
val test14 = test13(1,2)
看到這里,我相信大家對lambda表達(dá)式有了一定了解
2立肘、高階函數(shù)
高階函數(shù)是將函數(shù)用作參數(shù)或返回值的函數(shù)边坤。我覺得這個(gè)非常有用,而且很好理解谅年,有一個(gè)例子很好解釋:
fun <T> lock(lock: Lock, body: () -> T): T {
lock.lock()
try {
return body()
}
finally {
lock.unlock()
}
}
body擁有函數(shù)類型茧痒, 所以它應(yīng)該是一個(gè)不帶參數(shù)并且返回 T類型值的函數(shù)。 它在 try-代碼塊內(nèi)部調(diào)用融蹂、被 lock保護(hù)旺订,其結(jié)果由lock()函數(shù)返回弄企。可能剛剛開始比較難理解耸峭,我們還是一起看看幾個(gè)實(shí)例桩蓉,會讓我們更加好理解一些淋纲,例如:
實(shí)例一
//接受另一個(gè)函數(shù)作為參數(shù)的函數(shù)劳闹,我們必須為該參數(shù)指定函數(shù)類型
fun <T> max(collection: Collection<T>, less: (T, T) -> Boolean): T? {
var max: T? = null
for (it in collection)
if (max == null || less(max, it))
max = it
return max
}
less就是你要傳遞的函數(shù)參數(shù),那么如何使用呢洽瞬,你不要小瞧上面的函數(shù)哦本涕!接下里讓我看看有哪些使用:
fun test10(a : Int) : String{
val numbers : List<Int> = mutableListOf(17,12,5,67,3,10)
var stings : List<String> = arrayListOf("asd","dd","a","dddd")
var times : List<String> = arrayListOf("15:00:01","15:00:00","4:10:00","17:00:00")
fun dateScale(date1 : String, date2 : String) : Boolean{
val df = SimpleDateFormat("HH:mm:ss")//創(chuàng)建日期轉(zhuǎn)換對象HH:mm:ss為時(shí)分秒,年月日為yyyy-MM-dd
val dt1 = df.parse(date1)//將字符串轉(zhuǎn)換為date類型
val dt2 = df.parse(date2)
if (dt1.getTime() > dt2.getTime())
//比較時(shí)間大小,如果dt1大于dt2
{
return true
}else{
return false
}
}
when(a){
0 -> {
//求Int集合里最大值伙窃,返回最大值
return@test10 max(numbers, {x,y -> x>y}).toString()
}
1 -> {
//求Int集合里最小值菩颖,返回最小值
return@test10 max(numbers, {x,y -> x<y}).toString()
}
2 -> {
//求字符串集合里長度最長的值,返回長度最長的字符
return@test10 max(stings, {x,y -> x.length>y.length}).toString()
}
3 -> {
//甚至你可以把你需要做的一些操作放在一個(gè)函數(shù)里進(jìn)行調(diào)用为障,例如:dateScale(x,y)
//求時(shí)間集合里最小時(shí)間晦闰,返回最小時(shí)間
return@test10 max(times, {x,y -> dateScale(x,y)}).toString()
}
//當(dāng)然你還可以實(shí)現(xiàn)很多很多,是不是感覺比java更加簡潔和易懂啊鳍怨,而且非常非常的節(jié)省代碼呻右,說實(shí)話我是喜歡上Kotlin風(fēng)格
else -> return@test10 ""
}
}
哈哈,有沒有發(fā)現(xiàn)真的比java能節(jié)省很多代碼鞋喇,如果你想用java實(shí)現(xiàn)上面方法声滥,當(dāng)然也可以實(shí)現(xiàn),只是你會發(fā)現(xiàn)java沒有Kotlin把代碼分析的如此清晰侦香,其中max方法你完全不用改變
示例二
上面返回的是單個(gè)值落塑,那么我是否可以寫個(gè)公共函數(shù)用來返回滿足條件的集合呢,當(dāng)然可以罐韩,只需要對max稍作修改憾赁,如下:
fun <T> meetCons(collection: Collection<T>, less: (T) -> Boolean): List<T?> {
val tmp: MutableList<T?> = mutableListOf()
for (it in collection)
if (less(it))
tmp.add(it)
return tmp
}
這樣,你就可以對集合進(jìn)行指定篩選出你需要的集合了散吵,調(diào)用:
fun test11() : String {
val stings : List<String> = arrayListOf("asd","dd","ass","dddd","125")
//返回所有字符長度為3的集合
//你可以這樣返回
// return meetCons(stings,less = {x -> x.length == 3}).toString()
//當(dāng)然你也可以省去less =
return meetCons(stings){x -> x.length == 3}.toString()
}
示例三
上面我將集合作為參數(shù)進(jìn)行傳遞缠沈,那么是否可以對集合進(jìn)行添加類似擴(kuò)展函數(shù)呢,答案是肯定错蝴。
我們將Collection<T>拿到方法名前洲愤,就相當(dāng)于對Collection<T>進(jìn)行擴(kuò)展了,當(dāng)然返回值啥地你都可與自由的擴(kuò)展了顷锰,是不是覺得很神奇和好用柬赐,例如:
fun <T> Collection<T>.meetCons(less: (T) -> Boolean): Collection<T?> {
val tmp: MutableList<T?> = mutableListOf()
for (it in this)
if (less(it))
tmp.add(it)
return tmp
}
那么如何調(diào)用呢,和擴(kuò)展函數(shù)類似調(diào)用方法官紫,例如;
fun test12() : String {
val stings : List<String> = arrayListOf("asd","dd","ass","dddd","125")
//閉包
var sum = ""
stings.filter { it.length == 3 }.forEach {
sum += it
}
//返回所有字符長度為3的集合
//我們可以這樣返回
// return stings.meetCons{x -> x.length == 3}.toString()
//當(dāng)然只有一個(gè)參數(shù)的時(shí)候肛宋,kotlin是有個(gè)約定的州藕,使用it代替單個(gè)參數(shù),這樣更加簡潔
return stings.meetCons{ it.length == 3 }.toString() + ">>" + sum
}
上面調(diào)用里有閉包調(diào)用酝陈,Lambda 表達(dá)式或者匿名函數(shù)可以訪問其 閉包 床玻,即在外部作用域中聲明的變量。 與 Java 不同的是可以修改閉包中捕獲的變量
匿名函數(shù)
匿名函數(shù)沉帮,沒有名字锈死,其他語法和常規(guī)函數(shù)類似,例如:
//匿名函數(shù)
val test13 = fun(x : Int, y : Int):Int { return x + y }
//調(diào)用匿名函數(shù)
val test14 = test13(1,2)
//匿名函數(shù)可以省略返回類型穆壕,類型會自動(dòng)推斷
val test15 = fun(x : Int, y : Int) = x + y
內(nèi)聯(lián)函數(shù)(inline待牵、noinline)
使用高階函數(shù)會帶來一些運(yùn)行時(shí)的效率損失:每一個(gè)函數(shù)都是一個(gè)對象,并且會捕獲一個(gè)閉包喇勋。 即那些在函數(shù)體內(nèi)會訪問到的變量缨该。 內(nèi)存分配(對于函數(shù)對象和類)和虛擬調(diào)用會引入運(yùn)行時(shí)間開銷。
但是在許多情況下通過內(nèi)聯(lián)化 lambda 表達(dá)式可以消除這類的開銷
//內(nèi)聯(lián)函數(shù)
inline fun test16(inlined: () -> Unit, noinline notInlined: () -> Unit) {
// ……
}
inline 修飾符影響函數(shù)本身和傳給它的 lambda 表達(dá)式:所有這些都將內(nèi)聯(lián)到調(diào)用處川背。
內(nèi)聯(lián)可能導(dǎo)致生成的代碼增加贰拿,但是如果我們使用得當(dāng)(不內(nèi)聯(lián)大函數(shù)),它將在性能上有所提升熄云,尤其是在循環(huán)中的“超多態(tài)(megamorphic)”調(diào)用處膨更。
使用inline可以是函數(shù)內(nèi)聯(lián),noinline 可以控制局部不內(nèi)聯(lián)皱碘,調(diào)用合適會在性能上有所提升询一,寫代碼的時(shí)候可以考慮這些,優(yōu)化代碼
源碼下載
這里源碼會隨著后面發(fā)布的Kotlin逐漸完善