<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Kotlin修煉指南(三)——奇技淫巧

          共 7581字,需瀏覽 16分鐘

           ·

          2020-08-19 03:00

          Kotlin作為Android開發(fā)的首選語(yǔ)言,為開發(fā)者提供了大量的語(yǔ)法糖和技巧,讓開發(fā)者可以專注于需求開發(fā),而將語(yǔ)言所帶來的影響減少到最少。Java和Kotlin最大的區(qū)別,實(shí)際上在于Kotlin的函數(shù)式編程思想以及語(yǔ)法,特別是lambda表達(dá)式,這是Kotlin效率高于Java開發(fā)的核心武器,在之前的文章中,已經(jīng)有比較詳細(xì)的講解了。

          下面我將從幾個(gè)方面分別來給大家演示下Kotlin究竟是如何提高開發(fā)效率的。

          語(yǔ)法糖

          所謂語(yǔ)法糖,實(shí)際上就是對(duì)Java原生寫法進(jìn)行的封裝,雖然不用也能寫,但是用了絕對(duì)回不去。

          字符串模版

          "${xxxBean.type}"

          字符串模版保證了String的完整性,這也是大部分現(xiàn)代語(yǔ)言都會(huì)有的功能,有了字符串模板,就可以不再使用+進(jìn)行拼接,不但更方便,也讓字符串的語(yǔ)義更加明確。

          Raw string

          val?str1?=?"abc"
          val?str2?=?"""value1\n
          ????value2
          ????value3
          ????"
          value4"
          ????"
          "".trimMargin()

          三引號(hào)代表Raw string,即三引號(hào)內(nèi)所有內(nèi)容均為string,即使有需要轉(zhuǎn)義的字符,也不用特殊處理。

          使用trimMargin來去除每行開頭的空格

          強(qiáng)化Switch

          Kotlin中的when函數(shù),解除了Java中的switch的很多限制,并且拓展了很多方便的功能。

          fun?getValue(a:?Int)?=?when?{
          ????a?>?0?->?"A"
          ????a??"N"
          ????a.hashCode()?==?0x108?->?"XYS"
          ????else?->?"ZJ"
          }

          在使用上更加靈活,同時(shí)讓選擇分支語(yǔ)句更加容易理解。

          語(yǔ)句 Vs 表達(dá)式

          kotlin中大部分關(guān)鍵字都是表達(dá)式。

          • 表達(dá)式有值,并且能作為另一個(gè)表達(dá)式的一部分使用,這是函數(shù)式編程的基礎(chǔ)
          • 語(yǔ)句總是包圍著它的代碼塊中的代碼元素,并且沒有自己的值,例如Java中的if\else\Switch等。

          在Kotlin中,一個(gè)if語(yǔ)句是可以直接給一個(gè)變量賦值的,這就是表達(dá)式,它有返回值。

          val?status?=?when?{}
          ????xxx?->?{}
          ????xxx?->?{}
          ????else?->?{}
          }

          這種方式比Java節(jié)省了太多的代碼,所以Kotlin中不再需要三目表達(dá)式了,直接通過if/else即可。

          fun?max(a:?Int,?b:?Int)?=?if?(a?>?b)?a?else?b

          延遲初始化

          在Kotlin中,成員變量的值被嚴(yán)格區(qū)分可空和非可空,其中非可空的變量值,要么在聲明的時(shí)候進(jìn)行初始化,要么通過延遲加載的方式進(jìn)行初始化,一般來說,有兩種方式來進(jìn)行延遲加載。

          lazy

          通過lazy函數(shù),可以實(shí)現(xiàn)在首次使用到的時(shí)候才去實(shí)例化。

          private?val?xxxxFragment?by?lazy?{
          ????XXXXFragment().apply?{
          ????????arguments?=?Bundle().apply?{
          ????????}
          ????}
          }

          lateinit

          通過lateinit,自己控制變量的初始化。

          private?lateinit?var?iv:?ImageView

          這兩種方式各有各的使用場(chǎng)景:

          • by lazy 修飾val的變量
          • lateinit 修飾var的變量,且變量是非空的類型

          data class

          data class是Kotlin中一個(gè)用來生成模板代碼的語(yǔ)法糖,在Java中,定義的實(shí)體類,通常會(huì)有很多的模板代碼,大部分情況下,我們都是通過一個(gè)工具插件來生成,而在Kotlin中,則更加簡(jiǎn)單。

          第一種方式實(shí)際上是Kotlin對(duì)構(gòu)造函數(shù)的優(yōu)化,省略了構(gòu)造函數(shù)的實(shí)體,直接通過參數(shù)聲明的方式進(jìn)行了創(chuàng)建。

          //?Kotlin會(huì)為類的參數(shù)自動(dòng)實(shí)現(xiàn)get?set方法
          class?User(val?name:?String,?val?age:?Int,?val?gender:?Int,?var?address:?String)

          第二種方式則是借助data關(guān)鍵字,生成Kotlin中定義好的實(shí)體類。

          //?用data關(guān)鍵詞來聲明一個(gè)數(shù)據(jù)類,除了會(huì)自動(dòng)實(shí)現(xiàn)get?set,同時(shí)還會(huì)自動(dòng)生成equals?hashcode?toString
          data?class?User(val?name:?String,?val?age:?Int,?val?gender:?Int,?var?address:?String)

          object

          object在Kotlin中是一個(gè)比較難理解的概念,和Java中的Object完全不同,后面會(huì)有單獨(dú)的文章來介紹object,這里先簡(jiǎn)單的看下Kotlin通過object提供的語(yǔ)法糖。

          object,其實(shí)可以把它理解成:定義一個(gè)類并創(chuàng)建該類的一個(gè)實(shí)例。

          所以object的一個(gè)功能,就是快速創(chuàng)建一個(gè)單例模式。

          例如在代碼中經(jīng)常寫的:

          object?ThreadUtil?{

          ????fun?onMainThread(runnable:?Runnable)?{
          ????????val?mainHandler?=?Handler(Looper.getMainLooper())
          ????????mainHandler.post(runnable)
          ????}
          }

          簡(jiǎn)化下實(shí)際上就是下面的代碼。

          object?Singleton?{

          ????fun?xxx()?{
          ????}
          }

          反編譯后看生成代碼,這就是一個(gè)典型的餓漢式單例,借助靜態(tài)代碼塊初始化的鎖,初始化單例實(shí)例,從而實(shí)現(xiàn)單例效果。

          public?final?class?Singleton?{
          ???public?static?final?Singleton?INSTANCE;

          ???public?final?void?xxx()?{
          ???}

          ???private?Singleton()?{
          ???}

          ???static?{
          ??????Singleton?var0?=?new?Singleton();
          ??????INSTANCE?=?var0;
          ???}
          }

          通過object代替匿名內(nèi)部類

          這是object的另一個(gè)比較常用的地方,也符合了object的語(yǔ)義,定義一個(gè)類,并生成該類的實(shí)例,也就是需要?jiǎng)?chuàng)建的匿名內(nèi)部類。

          viewPager.addOnPageChangeListener(object?:?ViewPager.OnPageChangeListener?{
          ????override?fun?onPageScrollStateChanged(state:?Int)?{}

          ????override?fun?onPageScrolled(position:?Int,?positionOffset:?Float,?positionOffsetPixels:?Int)?{}

          ????override?fun?onPageSelected(position:?Int)?{}
          });

          companion object

          由于Kotlin中沒有靜態(tài)函數(shù),所以在Kotlin中,可以使用companion object替代Java中的static修飾。

          編譯器會(huì)自動(dòng)生成了一個(gè)叫做Companion的靜態(tài)內(nèi)部類。

          在Java中調(diào)用伴生對(duì)象,可以使用User.Companion.isMale(1)

          class?User?{

          ????companion?object?{
          ????????const?val?DEFAULT_USER_AGE?=?30
          ????}
          ????
          ????fun?test(){}
          }

          //?later,?accessed?like?you?would?a?static?variable:
          user.age?=?User.DEFAULT_USER_AGE

          Kotlin函數(shù)

          在Kotlin的基礎(chǔ)庫(kù)中,系統(tǒng)提供了大量針對(duì)函數(shù)的優(yōu)化,解決了很多在Java代碼中寫起來不太爽的地方。

          顯式參數(shù)

          在Java中,當(dāng)一個(gè)函數(shù)的參數(shù)值太多時(shí),需要一個(gè)個(gè)對(duì)齊參數(shù),雖然可以通過IDE的快捷提示等功能來展示,但始終用起來不太方便,而在Kotlin中,除了像Java中那樣按順序的傳遞參數(shù)外,還可以通過指定參數(shù)名的方式進(jìn)行參數(shù)傳遞。

          fun?test(name:?String,?age:?Int)?{
          }

          test(name?=?"xys",?age?=?18)

          參數(shù)含義一目了然,提高了代碼的可讀性。

          參數(shù)默認(rèn)值

          fun?test(name:?String?=?"xys",?age:?Int)?{
          }

          fun?a()?{
          ????test(age?=?18)
          }

          通過參數(shù)默認(rèn)值,可以避免Java下大量參數(shù)下的重載函數(shù),當(dāng)某個(gè)參數(shù)可以使用默認(rèn)值時(shí),就不用顯示的聲明了,類似Java中的不同參數(shù)的重載函數(shù)。

          在Java、Kotlin混編的時(shí)候,無(wú)法避免的會(huì)混合調(diào)用,可以通過@JvmOverloads注解,給Java代碼生成重載的函數(shù)。

          拓展函數(shù)

          拓展函數(shù)可以說是Kotlin最為重要的黑魔法之一了,通過拓展函數(shù),可以給一些系統(tǒng)類添加原本沒有的函數(shù),極大的提高了函數(shù)的可拓展性。

          fun?Activity.toast(msg:?String)?{
          ????Toast.makeText(this,?msg,?Toast.LENGTH_SHORT).show()
          }

          拓展屬性

          與拓展函數(shù)類似,拓展屬性可以給現(xiàn)有屬性拓展自定義的實(shí)現(xiàn)。

          val?String.lastChar:?Char
          ????get()?=?get(length?-?1)

          拓展功能看上去比較神奇,但大家可以通過查看Kotlin生成的class代碼,反編譯成的Java代碼來看它具體的實(shí)現(xiàn)方法。

          對(duì)于擴(kuò)展函數(shù)來說,轉(zhuǎn)化為Java代碼的時(shí)候,其實(shí)就是生成一個(gè)靜態(tài)的函數(shù),這個(gè)靜態(tài)函數(shù)的第一個(gè)參數(shù)就是該類的實(shí)例對(duì)象,所以這樣把類的實(shí)例傳入函數(shù)以后,函數(shù)內(nèi)部就可以訪問到類的公有方法。

          擴(kuò)展屬性也是類似,獲取的擴(kuò)展屬性會(huì)生成為一個(gè)靜態(tài)的get函數(shù),同時(shí)這個(gè)靜態(tài)函數(shù)的第一個(gè)參數(shù)就是該類的實(shí)例對(duì)象,設(shè)置的擴(kuò)展屬性會(huì)轉(zhuǎn)化為一個(gè)靜態(tài)的set函數(shù),同時(shí)這個(gè)靜態(tài)函數(shù)的第一個(gè)參數(shù)就是該類的實(shí)例對(duì)象。函數(shù)內(nèi)部可以訪問公有的方法和屬性。

          在了解了其實(shí)現(xiàn)原理后,可以發(fā)現(xiàn),拓展函數(shù)一定是static的,且不能被override,也不存在運(yùn)行時(shí)類型,其類型在編譯時(shí)就已經(jīng)確定,同時(shí)擴(kuò)展函數(shù)和擴(kuò)展屬性內(nèi)只能訪問到類的公有方法和屬性,私有的和protected同樣是不能訪問的。

          拓展函數(shù)和拓展屬性只是Kotlin語(yǔ)法的障眼法,并沒有實(shí)際的去修改一個(gè)類

          嵌套函數(shù)

          函數(shù)是Kotlin中的第一公民,所以函數(shù)可以出現(xiàn)在Kotlin中的任何一個(gè)地方,包括在一個(gè)函數(shù)中。

          在一個(gè)函數(shù)中定義另一個(gè)函數(shù),可以很好的將這個(gè)函數(shù)的使用限制在當(dāng)前的外層函數(shù)中,避免對(duì)外暴露不必要的接口,同時(shí)還能避免重復(fù)的模板代碼,例如下面這個(gè)例子。

          class?User(val?id:?Int,?val?name:?String,?val?address:?String,?val?email:?String)

          fun?check(user:?User)?{
          ????if?(user.name.isEmpty())?{
          ????????throw?IllegalArgumentException("Can't?save?user?${user.id}:?empty?Name")
          ????}
          ????if?(user.address.isEmpty())?{
          ????????throw?IllegalArgumentException("Can't?save?user?${user.id}:?empty?Address")
          ????}
          ????if?(user.email.isEmpty())?{
          ????????throw?IllegalArgumentException("Can't?save?user?${user.id}:?empty?Email")
          ????}
          ????//?...
          }

          通過嵌套函數(shù)實(shí)現(xiàn)。

          fun?saveUser2(user:?User)?{
          ????fun?validate(value:?String,?fildName:?String)?{
          ????????if?(value.isEmpty())?{
          ????????????throw?IllegalArgumentException("Can't?save?user?${user.id}:?empty?$fildName")
          ????????}
          ????}

          ????validate(user.name,?"Name")
          ????validate(user.address,?"Address")
          ????validate(user.email,?"Email")
          ????//?...
          }

          工具類函數(shù)

          由于在Kotlin中,函數(shù)可以脫離類而獨(dú)立存在,所以這對(duì)于工具類函數(shù)來說,就非常方便了,不用再定義一個(gè)ToolUtil類,而可以直接寫在文件中。

          作用域函數(shù)

          作用域函數(shù)在Kotlin修煉指南(一)中已經(jīng)有詳細(xì)介紹了。

          設(shè)計(jì)模式

          設(shè)計(jì)模式最早是在面向?qū)ο缶幊痰幕A(chǔ)上提出來的編程范式,但是對(duì)于函數(shù)式編程來說,有很多定義都過于教條了,所以,現(xiàn)代式的編程語(yǔ)言,通過很多語(yǔ)法上的定義,就已經(jīng)實(shí)現(xiàn)了很多種設(shè)計(jì)模式。

          單例模式

          前面已經(jīng)提到了,通過object class,就可以很輕松的實(shí)現(xiàn)一個(gè)線程安全的單例類。

          靜態(tài)工廠模式

          借助運(yùn)算符重載,可以很方便的實(shí)現(xiàn)靜態(tài)工廠模式。

          interface?Car?{
          ????val?brand:?String

          ????companion?object?{
          ????????operator?fun?invoke(type:?CarType):?Car?{
          ????????????return?when?(type)?{
          ????????????????CarType.AUDI?->?Audi()????
          ????????????????CarType.BMW?->?BMW()
          ????????????}
          ????????}
          ????}
          }

          通過重載了invoke()函數(shù),在調(diào)用Car(CarType.BMW)的時(shí)候,就創(chuàng)建好了對(duì)應(yīng)的工廠實(shí)例。

          代理模式 策略模式

          代理模式,或者說策略模式,都可以通過Kotlin中的類委托來實(shí)現(xiàn)。

          interface?BaseTTS?{
          ????fun?doTTS()
          }

          class?BaiDuTTS?:?BaseTTS?{
          ????override?fun?doTTS()?{
          ????????print("BaiDu")
          ????}
          }

          class?TencentTTS?:?BaseTTS?{
          ????override?fun?doTTS()?{
          ????????print("Tencent")
          ????}
          }

          class?TTSCategory(tts:?BaseTTS)?:?BaseTTS?by?tts

          fun?doTest()?{
          ????TTSCategory(BaiDuTTS()).doTTS()
          }

          通過類委托,將tts的實(shí)現(xiàn)代理出來。

          更進(jìn)一步,可以通過匿名類的方式,直接創(chuàng)建代理類的實(shí)現(xiàn)。

          interface?BaseTTS?{
          ????fun?doTTS()
          }

          class?TTSCategory(tts:?BaseTTS)?:?BaseTTS?by?tts?{
          ????override?fun?doTTS()?{
          ????????print("Do?tts")
          ????}
          }

          而當(dāng)策略中只有一個(gè)函數(shù)的時(shí)候,還可以進(jìn)一步簡(jiǎn)化,把策略直接封裝成Lambda表達(dá)式。

          class?TTSCategory(val?strategy:?()?->?Unit)?{
          ????fun?doTTS()?{
          ????????strategy.invoke()
          ????}
          }

          fun?test()?{
          ????TTSCategory?{?print("Do?tts")?}.doTTS()
          }

          裝飾器模式

          同樣是通過類委托功能,還可以實(shí)現(xiàn)裝飾器模式。

          裝飾器模式是為了解決繼承導(dǎo)致類行為變更的問題產(chǎn)生的。如果需要在使用一個(gè)類的同時(shí),又要修改該類的一些函數(shù)的實(shí)現(xiàn),這時(shí)候就可以使用裝飾器模式,創(chuàng)建一個(gè)裝飾器類,實(shí)現(xiàn)與原始類一樣的接口并將原來的類的實(shí)例作為一個(gè)成員變量。裝飾器類與原始類擁有相同行為的方法不用修改,只需要直接轉(zhuǎn)發(fā)給原始類的實(shí)例,需要修改的函數(shù),實(shí)現(xiàn)新的功能即可。

          但這里的問題是,當(dāng)一個(gè)原始類需要實(shí)現(xiàn)的函數(shù)很多時(shí),而裝飾器類又只需要修改很少的函數(shù)時(shí),就會(huì)產(chǎn)生大量的模板代碼,所以這個(gè)時(shí)候,借助類委托,就可以極大的減少這種模板代碼的產(chǎn)生。

          class?ListDecorator(val?innerSet:?List?=?listOf())?:?List?by?innerSet?{
          ????override?fun?contains(element:?T):?Boolean?{
          ????????print("Do?other?thing")
          ????????return?innerSet.contains(element)
          ????}
          }

          fun?test()?{
          ????val?contains?=?ListDecorator(listOf("ss")).contains("s")
          }

          通過反編譯代碼可以發(fā)現(xiàn),實(shí)際上編譯器幫助我們重寫了所有的未修改函數(shù)。

          后續(xù)計(jì)劃

          Kotlin有趣的地方還有很多,一篇文章很難全部寫完,所以后面的計(jì)劃如下。

          • 集合與惰性序列
          • Kotlin DSL
          • 操作符重載
          • sealed class
          • KTX

          修仙

          Flutter Dojo開源至今,受到了很多Flutter學(xué)習(xí)者和愛好者的喜愛,也有越來越多的人加入到Flutter的學(xué)習(xí)中來,所以我建了個(gè)Flutter修仙群,但是人數(shù)太多,所以分成了【Flutter修仙指南】【Flutter修仙指北】【Flutter修仙指東】三個(gè)群,對(duì)Flutter感興趣的朋友,可以添加我的微信,注明加入Flutter修仙群,或者直接關(guān)注我的微信公眾號(hào)【Android群英傳】。

          感興趣的朋友可以加我微信【Tomcat_xu】,我拉你入群。

          項(xiàng)目地址:

          https://github.com/xuyisheng/flutter_dojo


          瀏覽 72
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  校园春色亚洲无码 | 中文字幕在线和永久在线的区别 | 看黄免费观看 | 成人视频偷拍 | 天天干天天很天天狠狠 |