Kotlin基本篇-徹底理解匿名函數(shù)和高階函數(shù)

Kotlin 總結(jié)分享

如果用一句話總結(jié)kotlin,那么就是:更好的java
類型申明

String a = "I an java";

Val a :String = "I an Kotlin"

為什么采用這種風(fēng)格辙培? 代碼的可讀性更好

增強的類型推到

val String = "I an Kotlin"
val int = 12
val long = 12L
val float = 13.14f
val double = 12.12
val double = 1.0e2 // 100

類型推導(dǎo)在很大程度上提高了開發(fā)效率,當(dāng)我們使用kotlin的時候,不再需要寫大量的類型

聲明函數(shù) 返回值類型

fun sum(x:Int,y:Int):Int{
    return x+y
}
fun sum(x:Int,y:Int) = x+y

val 和var的使用規(guī)則
var代表了 變量罗岖,val 具有java中的final 關(guān)鍵字的效果,引用不可變腹躁,但是其引用的值是可以更改的

val bool = Book() //用val聲明的book對象的引用不可變 
book.name = "kt"

優(yōu)先使用val 聲明一個本身不可變的變量是一種防御性編碼思維桑包,更加安全可靠,不可變的變量意味著更加容易推理纺非,越是復(fù)雜的業(yè)務(wù)邏輯哑了,優(yōu)勢越大,但是如何保證引用對象的不可變烧颖?--- 參考集合

高階函數(shù)與lambda

函數(shù)試語言一個典型的特征就在于 函數(shù)是頭等公民 ---- 我們不僅可以像類一樣在頂層直接定義一個函數(shù)弱左,還可以在一個函數(shù)內(nèi)部定義一個局部函數(shù)

fun foo(x:Int){
        fun double(y:Int) = y*2
        print(double(x))
    }

>>> foo(1)
2

此外,我們還可以直接將函數(shù)像普通變量一樣傳遞給另一個函數(shù)炕淮,或在其他函數(shù)內(nèi)被返回

實例:函數(shù)作為參數(shù)的需求

現(xiàn)有關(guān)于國家的數(shù)據(jù)集List<Country> 如何篩選出歐洲的國家拆火?

data class Country(
    val name:String,
    val continent:String,
    val population:Int
)
class  CountryApp{
    fun filterCountries(countries:List<Country>):List<Country>{
        val res = mutableListOf<Country>()
        for (c in countries){
            if(c.continent == "EU"){
                res.add(c)
            }
        }
        return res
    }
}

需求變化一:不僅想要歐洲,還想要亞洲 如何修改

fun filterCountries(countries:List<Country>,continent: String):List<Country>{
        val res = mutableListOf<Country>()
        for (c in countries){
            if(c.continent == continent){
                res.add(c)
            }
        }
        return res
    }

需求變化二:不僅需要歐洲,亞洲等榜掌,還需要篩選一些 具有人口規(guī)模的國家

fun filterCountries(countries:List<Country>,continent: String,population: Int):List<Country>{
        val res = mutableListOf<Country>()
        for (c in countries){
            if(c.continent == continent && c.population>=population){
                res.add(c)
            }
        }
        return res
    }

如果按照現(xiàn)有的修改优妙,更多的篩選條件會作為方法參數(shù)不斷的累加,而且業(yè)務(wù)邏輯高度耦合憎账,解決問題的核心在于對這個方法進行解耦合 java做法:傳入一個類對象套硼,根據(jù)不同的篩選需求創(chuàng)建不同的子類(貌似就是策略模式),對于 kotlin 胞皱,支持高階函數(shù)邪意,我們可以把篩選邏輯變成一個方法傳入,這種思路更加簡單

為了了解高級特性反砌,所以假如有一個新的測試類

class CountryTest{
    fun isBigEuropeanCountry(country: Country):Boolean{
        return country.continent == "EU" && country.population>1000000
    }
}

調(diào)用isBigEuropeanCountry 方法就能夠判斷一個國家是否是一個人口超過百萬的歐洲國家雾鬼,那么怎么才能把這個方法變成 filterCountries 方法的一個參數(shù)呢?要解決以下兩個問題
· 方法作為參數(shù)傳入宴树,必須像其他參數(shù)一樣具備具體的類型信息 (也就是說 需要一個函數(shù)類型)
· 需要把isBigEuropeanCountry 的方法引用當(dāng)作參數(shù) 傳遞給 filterCountries

函數(shù)的類型

格式:(Int)-> Unit
左邊是參數(shù)類型策菜,右邊是返回值類型

(a:Int,b:Int) ->Int 
  (Int ,String) ->String
(Int)->((Int) ->Unit)     // 返回類型是一個函數(shù)也是可以的

有了函數(shù)類型,那么就可以修改filterCountries 方法了

fun filterCountries(countries:List<Country>,filter:(Country)->Boolean):List<Country>{
        val res = mutableListOf<Country>()
        for (c in countries){
            if(filter(c)){
                res.add(c)
            }
        }
        return res
    }

    fun isBigEuropeanCountry(country: Country):Boolean{
        return country.continent == "EU" && country.population>1000000
    }

雖然已經(jīng)改造了篩選方法 但是我現(xiàn)在已經(jīng)有了一個篩選策略(isBigEuroupeanCountry)如何把這個函數(shù)傳進去呢酒贬? 也許你想這么用

filterCountries(countries, isBigEuropeanCountry) // 很遺憾這里第二個參數(shù)會報錯 又憨,原因是類型不匹配
//凌亂了,不是說 函數(shù)可以當(dāng)成參數(shù)來傳遞嗎锭吨,為啥這里不行

方法和成員引用
kotlin 存在一種特殊的語法蠢莺,通過兩個冒號來實現(xiàn)對于某個類的方法進行引用,假如我們有一個CountryTest類的實例對象 countryTest 零如,如果要引用他的isBigEuropeanCountry方法躏将,就可以這么寫

countryTest::isBigEuropeanCountry

在kotlin中函數(shù)是頭等公民,那么怎么直接對方法引用呢

::isBigEuropeanCountry

所以上面的使用我們就可以這樣寫

filterCountries(countries, ::isBigEuropeanCountry) // 這里不會再報錯

經(jīng)過這樣的重構(gòu)考蕾,程序顯然比之前優(yōu)雅許多祸憋,可以根據(jù)任意的篩選需求,調(diào)用同一個filterCountries 方法來獲取國家數(shù)據(jù)辕翰。

匿名函數(shù)

繼續(xù)思考一下篩選方法夺衍,每新增一個需求狈谊,就需要寫一個新的篩選方法喜命,但是很多都是零食性的,不需要被復(fù)用河劝,于是匿名函數(shù)就派上用場壁榕,kotlin支持在缺省函數(shù)名的情況下,直接定義一個函數(shù)赎瞎,所以isBigEuropeanCountry方法我們可以直接定義為

 fun (country: Country):Boolean{ // 沒有函數(shù)名字
        return country.continent == "EU" && country.population>100000
    }

當(dāng)我們在編譯器里這么寫一個匿名函數(shù) 它會報錯 Function declaration must have a name 牌里,又凌亂了,不是可以定義匿名函數(shù)嗎,為啥又要有名字牡辽,可見 匿名函數(shù)不是這么用的

那這個匿名函數(shù)有啥用喳篇? 其實匿名函數(shù)是用來傳遞的,他的使用方式是這樣

filterCountries(countries, fun(country:Country):Boolean{ return country.continent == "EU" && country.population>100000})

知道了匿名函數(shù)态辛,我們再來看一下lambda是什么麸澜,由匿名函數(shù)可以知道,由于要傳入的匿名函數(shù) 早已經(jīng)在參數(shù)中聲明了參數(shù)和返回值奏黑,那么匿名函數(shù)中的參數(shù)和返回值是不是可以也不要了

fun filterCountries(countries:List<Country>?,filter:(Country)->Boolean):List<Country>{//這里可以推導(dǎo)出傳入的參數(shù)和返回的類型
        val res = mutableListOf<Country>()
        for (c in countries!!){
            if(filter(c)){
                res.add(c)
            }
        }
        return res
    }

    fun test(){

        filterCountries(countries, fun(country:Country):Boolean{ return country.continent == "EU" && country.population>100000})
        filterCountries(null, {country ->  country.continent == "EU" && country.population>100000 })
    }

當(dāng)我們只保留需要的內(nèi)容 炊邦,就形成了lambda表達式 {參數(shù)變量 -> 返回值}
lambda 的表達是有自己的規(guī)則

  • 一個lambda必須通過{} 來包裹
  • 如果Lambda聲明了參數(shù)部分的類型,且返回值類型支持類型推導(dǎo)熟史,那么Lambda變量就可以省略函數(shù)類型聲明
val sum:(Int,Int) ->Int = {x:Int,y:Int ->x+y} // 完整寫法
val  sum = {x:Int,y:Int -> x+y} // 推導(dǎo)類型寫法
val sum:(x:Int,y:Int)->Int = {x,y ->x+y} // 省略參數(shù)部分類型
  • 如果Lambda 變量聲明了函數(shù)類型馁害,那么Lambda的參數(shù)部分類型就可以省略
  • 此外 Lambda表達式返回不是Unit,那么默認最后一行表達式的值類型就是返回值類型

一個例子來看匿名函數(shù)蹂匹,Lambda究竟是什么

fun hello(int: Int)  = { // 用Lambda初始化了一個函數(shù)
   print(int)
}
hello(12) // 調(diào)用

上述例子會打印12 嗎 不會碘菜,因為foo(12)它不是函數(shù),也就是說 Lambda 不是函數(shù)限寞,他其實是一個對象炉媒,我們通過查看其Java代碼

private final Function0 hello(final int a) {
      return (Function0)(new Function0() {
         // $FF: synthetic method
         // $FF: bridge method
         public Object invoke() {
            this.invoke();
            return Unit.INSTANCE;
         }

         public final void invoke() {
            int var1 = a + TT2.INSTANCE.getCc();
            boolean var2 = false;
            System.out.print(var1);
         }
      });
   }

通過把kotlin編譯成Java代碼發(fā)現(xiàn) hello這個方法里返回了一個Function0 類的匿名內(nèi)部類的對象,并且其內(nèi)部有invoke方法昆烁,那么我們想讓上述調(diào)用打印出12 應(yīng)該這樣寫

hello(12).invoke // 這個時候會打印出12 

通過這個我們發(fā)現(xiàn) 匿名函數(shù)(簡寫 后成Lambda)都不是函數(shù)吊骤,而是對象,由于他們是對象静尼,所以能在函數(shù)中當(dāng)成參數(shù)傳遞白粉,這也就是高階函數(shù)的本質(zhì)

Function類型

kotlin 在JVM層設(shè)計了Function類型(Function0 Function1 ... Function22)來兼容Java的Lambda表達式,后綴數(shù)字代表了Lambda參數(shù)的數(shù)量鼠渺,每一個Function類型都有一個invoke方法

中綴表達式

kotlin中的中綴表達式有些什么鸭巴?

for (i in 1..10) print(i)   //[1,10]
for( i in 1..10 step 2) println(i) // 1,3,5,7,9

 for (i in 10 downTo 1) print(" $i") // 倒敘
 for(i in 1 until  10) print(" $i") // [1,10)

 val array:IntArray = IntArray(10)
 for ((index,value) in array.withIndex()) // 數(shù)組index value 一起遍歷

以上這些奇特方法 如 in,step, downTo ,until 它們可以不通過點號拦盹,而是通過中綴表達式來被調(diào)用鹃祖,從而讓語法變得更加簡潔直觀。
自定義一個中綴表達式

class Person(val name:String,val age:Int)
infix fun Person.vs(person: Person){
    when {
        age - person.age >0 -> {
            print("$name 比 ${person.name} 年長")
        }
        age == person.age -> {
            print("$name 和 ${person.name} 一樣大")
        }
        else -> {
            print("$name 比 ${person.name} 小")
        }
    }
}

val zhansan = Person("張三",18)
val lisi = Person("李四",19)
val wangwu = Person("王五",18)
zhansan vs lisi  //張三 比 李四 小

如果把復(fù)雜條件寫成中綴的形式普舆,會讓代碼看起來特別簡潔

定義中綴函數(shù)的條件

  • 中綴函數(shù)必須是某個類型的擴展函數(shù)或成員方法
  • 中綴函數(shù)只能有一個參數(shù) (有且只有一個參數(shù))
    中綴的形式 A 中綴方法 B(比如 張三 vs 李四)
    由于to會返回Pair這種鍵值對的數(shù)據(jù)結(jié)構(gòu)恬口,因此我們經(jīng)常會與map結(jié)合一起使用
mapOf(1 to "one",2 to "two")

字符串

定義原生字符串 使用"""

 val c = """ \nAAA """   // 這里的\n 不會轉(zhuǎn)義 定義html比較方便
 val a ="A\nA" // 
val c = "A${}B"  // 字符串模版寫法 {表達式}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市沼侣,隨后出現(xiàn)的幾起案子祖能,更是在濱河造成了極大的恐慌,老刑警劉巖蛾洛,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件养铸,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機钞螟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門兔甘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鳞滨,你說我怎么就攤上這事裂明。” “怎么了太援?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵闽晦,是天一觀的道長。 經(jīng)常有香客問我提岔,道長仙蛉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任碱蒙,我火速辦了婚禮荠瘪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赛惩。我一直安慰自己哀墓,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布喷兼。 她就那樣靜靜地躺著篮绰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪季惯。 梳的紋絲不亂的頭發(fā)上吠各,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天,我揣著相機與錄音勉抓,去河邊找鬼贾漏。 笑死,一個胖子當(dāng)著我的面吹牛藕筋,可吹牛的內(nèi)容都是我干的纵散。 我是一名探鬼主播,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼隐圾,長吁一口氣:“原來是場噩夢啊……” “哼伍掀!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起翎承,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤硕盹,失蹤者是張志新(化名)和其女友劉穎符匾,沒想到半個月后叨咖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年甸各,在試婚紗的時候發(fā)現(xiàn)自己被綠了垛贤。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡趣倾,死狀恐怖聘惦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情儒恋,我是刑警寧澤善绎,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站诫尽,受9級特大地震影響禀酱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜牧嫉,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一剂跟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧酣藻,春花似錦曹洽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至怕轿,卻和暖如春坊夫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背撤卢。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工环凿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人放吩。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓智听,卻偏偏與公主長得像,于是被迫代替她去往敵國和親渡紫。 傳聞我的和親對象是個殘疾皇子到推,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,647評論 2 354

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

  • 1. Kotlin介紹 基于JVM√枧欤可以與Java進行混合開發(fā)和相互調(diào)用莉测。由JetBrains開發(fā)。系出名門唧喉,也得...
    RobinYeung閱讀 1,135評論 0 0
  • 第2章 Kotlin 語法基礎(chǔ) 人與人之間通過語言來交流溝通捣卤,互相協(xié)作忍抽。人與計算機之間怎樣“交流溝通”呢?答案是編...
    光劍書架上的書閱讀 1,615評論 0 6
  • 前言 人生苦多董朝,快來 Kotlin 鸠项,快速學(xué)習(xí)Kotlin! 什么是Kotlin子姜? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,201評論 9 118
  • Kotlin的優(yōu)勢 代碼簡潔高效祟绊、強大的when語法,不用寫分號結(jié)尾哥捕,findViewById光榮退休牧抽,空指針安全...
    Windy_816閱讀 1,287評論 1 6
  • 基礎(chǔ)語法 1.1 增強類型推導(dǎo) 類型推導(dǎo)是Kotlin在java語言上的增強。編譯器可以在不顯示聲明情況推導(dǎo)出類型...
    zcwfeng閱讀 1,050評論 0 2