Kotlin標(biāo)準(zhǔn)函數(shù)與靜態(tài)方法基礎(chǔ)知識詳解
標(biāo)準(zhǔn)函數(shù)with與run和apply with函數(shù)
with函數(shù)
接收兩個參數(shù):第一個參數(shù)可以是任意類型的對象,第二個參數(shù)是一個Lambda表達(dá)式。with函數(shù)會在Lambda表達(dá)式中提供第一個參數(shù)對象的上下文,并使用Lambda表達(dá)式中的最后一行代碼作為返回值返回。
示例代碼如下:
val result=with(obj){ //這里是obj的上下文 "value"http://with函數(shù)的返回值 }
那么這個函數(shù)有什么作用呢?它可以在連續(xù)調(diào)用同一個對象的多個方法時讓代碼變得更加精簡,下面我們來看一個具體的例子。
比如有一個水果列表,現(xiàn)在我們想吃完所有的水果,并將結(jié)果打印出來,可以這樣寫
val list= listOf("Apple","Banana","Orange","Pear","Grape") val builder=StringBuilder() builder.append("Start eating fruits.\n") for(fruit in list){ builder.append(fruit).append("\n") } builder.append("Ate all fruits.") val result=builder.toString() println(result)
這段代碼的邏輯很簡單,就是使用StringBuilder來構(gòu)建吃水果的字符串,最后將結(jié)果打印出來
打印結(jié)果為:
觀察上述代碼,你會發(fā)現(xiàn)我們連續(xù)調(diào)用了很多次builder對象的方法。其實(shí)這個時候就可以考慮使用with函數(shù)來讓代碼變得更加精簡,如下所示:
val list= listOf("Apple","Banana","Orange","Pear","Grape") val result= with(StringBuilder()){ append("Start eating fruits.\n") for(fruit in list){ append(fruit).append("\n") } append("Ate all fruits.") toString() } println(result)
首先我們給with函數(shù)的第一個參數(shù)傳入一個StringBuilder對象,那么接下來整個Lambda表達(dá)式的上下文就會是這個StringBuilder對象。于是我們在Lambda表達(dá)式中就不用再像剛才那樣調(diào)用builder.append()和builder.toString()方法了,而是可以直接調(diào)用append()和toString()方法。Lambda表達(dá)式最后一行代碼會作為with函數(shù)的返回值返回,最終我們將結(jié)果打印出來。
雖然兩段代碼結(jié)果一樣,但是明顯第二段代碼的寫法更加簡潔一些,這就是with函數(shù)的作用。
run函數(shù)
run函數(shù)的用法和使用場景其實(shí)和with函數(shù)時非常類似的,只是稍微做了一些語法改動而已。首先run函數(shù)是不能直接調(diào)用的,而是一定要調(diào)用某個對象的run函數(shù)才行;其次run函數(shù)只接收一個Lambda參數(shù),并且會在Lambda表達(dá)式中提供調(diào)用對象的上下文。其他方面和with函數(shù)一樣,包括也會使用Lambda表達(dá)式最后一行作為返回值返回。示例代碼如下:
val result=obj.run{ //這里是obj的上下文 "value"http://run函數(shù)的返回值 }
那么現(xiàn)在我們就可以使用run函數(shù)來修改一下吃水果的這段代碼,如下所示
val list= listOf("Apple","Banana","Orange","Pear","Grape") val result=StringBuilder().run{ append("Start eating fruits.\n") for(fruit in list){ append(fruit).append("\n") } append("Ate all fruits.") toString() } println(result)
總體來說變化很小,只是將調(diào)用with函數(shù)并傳入StringBuilder對象改成了調(diào)用StringBuilder對象的run方法,其他沒什么區(qū)別,這兩段代碼最終的執(zhí)行結(jié)果是完全相同的。
apply函數(shù)
apply函數(shù)和run函數(shù)也是極其相似的,都是在某個對象上調(diào)用,并且接收一個Lambda參數(shù),也會在Lambda表達(dá)式中提供調(diào)用對象的上下文,但是apply函數(shù)無法指定返回值,而是會自動返回調(diào)用對象本身。示例如下:
val result=obj.apply{ //這里是obj的上下文 } //result==obj
那么現(xiàn)在我們再使用apply函數(shù)來修改一下吃水果的這段代碼
val list= listOf("Apple","Banana","Orange","Pear","Grape") val result=StringBuilder().apply{ append("Start eating fruits.\n") for(fruit in list){ append(fruit).append("\n") } append("Ate all fruits.") } println(result.toString())
由于apply函數(shù)無法指定返回值,只能返回調(diào)用對象本身,因此result實(shí)際上是一個StringBuilder對象,所以我們在最后打印的時候還要調(diào)用它的toString()方法才行。這段代碼的執(zhí)行結(jié)果和前面兩段仍然完全相同的。
其實(shí)with、run和apply這幾個函數(shù)的用法和使用場景是非常類似的。在大多數(shù)情況下,可以相互轉(zhuǎn)換。
例如我們啟動Activity的時候
val intent=Intent(this,SecondActivity::class.java) intent.putExtra("param1","data1") intent.putExtra("param2","data2") startActivity(intent)
這里每傳遞一個參數(shù)就會調(diào)用一次intent.putExtra()方法,如果要傳遞10個參數(shù),那就得調(diào)用10次。對于這種情況,我們就可以使用標(biāo)準(zhǔn)函數(shù)來對代碼進(jìn)行精簡,如下所示
val intent=Intent(this,SecondActivity::class.java).apply{ intent.putExtra("param1","data1") intent.putExtra("param2","data2") } startActivity(intent)
可以看到,由于Lambda表達(dá)式中的上下文就是Intent對象,所以我們不需要調(diào)用intent.putExtra()方法,而是直接調(diào)用putExtra()方法就可以了。傳遞的參數(shù)越多,這種寫法優(yōu)勢也就越明顯。
定義靜態(tài)方法
靜態(tài)方法再某些編程語言里面又叫做類方法,指的就是那種不需要創(chuàng)建實(shí)例就能調(diào)用的方法,所有主流的編程語言都會支持靜態(tài)方法這個特性。
在java中定義一個靜態(tài)方法,只需要在方法上聲明一個static關(guān)鍵字就可以了,如下所示:
public class Util{ public static void doAction(){ System.out.printlin("do action") } }
上述代碼中的doAction()方法就是一個靜態(tài)方法。調(diào)用靜態(tài)方法并不需要創(chuàng)建類的實(shí)例,而是可以直接以Util.doAction()這種寫法調(diào)用。因而靜態(tài)方法非常適合用于編寫一些工具類的功能,因為工具類通常沒有創(chuàng)建實(shí)例的必要,基本上是全局通用的。
和大多數(shù)主流編程語言不同的是,kotlin卻極度弱化了靜態(tài)方法這個概念,想要在Kotlin中定義一個靜態(tài)方法反倒不是那么容易。
因為Kotlin提供了比靜態(tài)方法更好用的語法特性,那就是單例類
object Util{ fun doAction(){ println("do action") } }
雖然這里的doAction不是靜態(tài)方法,但是我們?nèi)匀豢梢允褂肬til.doAction()方式來調(diào)用,這就是單例類所帶來的便利。
不過,使用單例類的寫法會將整個類中所有方法全部變成類似于靜態(tài)方法的調(diào)用方式,而如果我們只是希望讓類中的某一個方法變成靜態(tài)方法的調(diào)用方式該怎么辦呢?這個時候就可以使用companion object了,示例如下:
class Util{ fun doAction1(){ println("do action1") } companion object{ fun doAction2(){ println("do action2") } } }
首先我們將Util從單例類改成一個普通類,然后在類中直接定義一個doAction1()方法,又在companion object中定義了一個doAction2()方法?,F(xiàn)在兩個方法就有了本質(zhì)的區(qū)別,因為doAction1()方法是一定要先創(chuàng)建Util類的實(shí)例才能調(diào)用的,而doAction2()方法可以直接使用Util.doAction2()的方式調(diào)用。
不過,doAction2()方法其實(shí)也不是靜態(tài)方法,companion object這個關(guān)鍵字實(shí)際上會在Util類的內(nèi)部創(chuàng)建一個伴生類,而doAction2()方法就是定義在這個伴生類里面的實(shí)例方法。只是Kotlin會保證Util類始終只會存在一個伴生類對象,因此調(diào)用Util.doAction2()方法實(shí)際上就是調(diào)用了Util類中伴生對象的doAction2()方法。
編譯成字節(jié)碼結(jié)果如下:
public final class Util { public static final Util.Companion Companion = new Util.Companion((DefaultConstructorMarker)null); public final void doAction1() { String var1 = "do action1"; boolean var2 = false; System.out.println(var1); } @Metadata( mv = {1, 1, 16}, bv = {1, 0, 3}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\b\u0086\u0003\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002J\u0006\u0010\u0003\u001a\u00020\u0004¨\u0006\u0005"}, d2 = {"Ltest/Util$Companion;", "", "()V", "doAction2", "", "kotlin01"} ) public static final class Companion { public final void doAction2() { String var1 = "do action2"; boolean var2 = false; System.out.println(var1); } private Companion() { } // $FF: synthetic method public Companion(DefaultConstructorMarker $constructor_marker) { this(); } } }
由此看出,Kotlin中確實(shí)沒有直接定義靜態(tài)方法的關(guān)鍵字,但是提供了一些語法特性來支撐類似于靜態(tài)方法調(diào)用的寫法,這些語法特性基本上可以 滿足我們平時的開發(fā)需求了。
如果需要真正的靜態(tài)方法,Kotlin提供了兩種實(shí)現(xiàn)方式:注解和頂層方法。
先來看注解,前面的單例類和companion object都只是在語法的形式上模仿了靜態(tài)方法的調(diào)用方式,實(shí)際上他們都不是真正的靜態(tài)方法。因此如果你在Java中以靜態(tài)方法的形式去調(diào)用的話,你會發(fā)現(xiàn)這些方法并不存在。而如果我們給單例類或companion object中的方法加上@JvmStatic注解,那么Kotlin編譯器就會將這些方法編譯成真正的靜態(tài)方法,如下所示:
class Util{ fun doAction1(){ println("do action1") } companion object{ @JvmStatic fun doAction2(){ println("do action2") } } }
編譯成字節(jié)碼,此時doAction2()方法為靜態(tài)方法
public final class Util { public static final Util.Companion Companion = new Util.Companion((DefaultConstructorMarker)null); public final void doAction1() { String var1 = "do action1"; boolean var2 = false; System.out.println(var1); } @JvmStatic public static final void doAction2() { Companion.doAction2(); } @Metadata( mv = {1, 1, 16}, bv = {1, 0, 3}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\b\u0086\u0003\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002J\b\u0010\u0003\u001a\u00020\u0004H\u0007¨\u0006\u0005"}, d2 = {"Ltest/Util$Companion;", "", "()V", "doAction2", "", "kotlin01"} ) public static final class Companion { @JvmStatic public final void doAction2() { String var1 = "do action2"; boolean var2 = false; System.out.println(var1); } private Companion() { } // $FF: synthetic method public Companion(DefaultConstructorMarker $constructor_marker) { this(); } } }
注意@JvmStatic注解只能加在單例類或Companion object中的方法上,如果你嘗試加在一個普通方法上,會直接提示語法錯誤。
由于doAction2()方法已經(jīng)成為真正的靜態(tài)方法,那么現(xiàn)在不管是在Kotlin中還是Java中,都可以使用Util.doAction2()的寫法來調(diào)用。
再來看頂層方法,頂層方法指的是那些沒有定義在任何類中的方法,比如我們編寫的main()方法。Kotlin編譯器會將所有的頂層方法全部編譯成靜態(tài)方法,因此只要你定義了一個頂層方法,那么它就一定是靜態(tài)方法。
想要定義一個頂層方法,首先要創(chuàng)建一個Kotlin文件。對著任意包名右擊→New→Kotlin File/Class,在彈出的對話框中輸入文件名即可。注意創(chuàng)建類型要選擇File,如圖所示
點(diǎn)擊“ok”完成創(chuàng)建,這樣剛剛的包名路徑下就會出現(xiàn)一個Helper.kt文件?,F(xiàn)在我們再這個文件中定義的任何方法都會是頂層方法,比如這里我就定義一個doSomething()方法吧,如下所示:
fun doSomething(){ println("do something") }
Kotlin會將所有的頂層方法全部編譯成靜態(tài)方法,那么我們要怎么調(diào)用這個doSomething()方法呢?
如果是在Kotlin中調(diào)用的話,所有的頂層方法都可以在任何位置被直接調(diào)用,不用管包名路徑,也不用創(chuàng)建實(shí)例,直接輸入doSomething()即可。
但如果在Java代碼中調(diào)用,你會發(fā)現(xiàn)找不到doSomething()這個方法,因為Java中沒有頂層方法這個概念,所有的方法必須定義在類中。那么這個doSomething()被藏在哪里呢?我們剛才創(chuàng)建的Kotlin文件名叫做Helper.kt,于是Kotlin編譯器會自動創(chuàng)建一個叫作Helperkt的Java類,doSomething()方法就是以靜態(tài)方法的形式定義在HelperKt類里面的,因此在Java中使用Helperkt.doSomething()的寫法來調(diào)用就可以了。
到此這篇關(guān)于Kotlin標(biāo)準(zhǔn)函數(shù)與靜態(tài)方法基礎(chǔ)知識詳解的文章就介紹到這了,更多相關(guān)Kotlin標(biāo)準(zhǔn)函數(shù)與靜態(tài)方法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android超詳細(xì)講解組件AdapterView的使用
AdapterView組件是一組重要的組件,AdapterView本身是一個抽象基類,它派生的子類在用法上十分相似,從AdapterView派生出的三個子類:AdsListView、AdsSpinner、AdapterViewAnimator,這3個子類依然是抽象的,實(shí)際運(yùn)用時需要它們的子類2022-03-03android ContentResolver獲取手機(jī)電話號碼和短信內(nèi)容
這篇文章主要為大家詳細(xì)介紹了android ContentResolver獲取手機(jī)電話號碼、短信內(nèi)容,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-07-07Android webview 內(nèi)存泄露的解決方法
這篇文章主要介紹了Android webview 內(nèi)存泄露的解決方法的相關(guān)資料,需要的朋友可以參考下2017-07-07關(guān)于Android 4.4相機(jī)預(yù)覽、錄像花屏的問題的解決方法
這篇文章主要介紹了關(guān)于Android 4.4相機(jī)預(yù)覽、錄像花屏的問題的解決方法,非常不錯,具有參考借鑒價值,需要的朋友參考下吧2016-12-12Android如何實(shí)現(xiàn)非本地圖片的點(diǎn)擊態(tài)
Android如何實(shí)現(xiàn)非本地圖片的點(diǎn)擊態(tài),本文提供了詳細(xì)的實(shí)現(xiàn)代碼,需要了解的朋友可以參考下2012-12-12AndroidStudio不自動添加新創(chuàng)建的文件到VCS的解決辦法
這篇文章主要介紹了AndroidStudio不自動添加新創(chuàng)建的文件到VCS的解決辦法的相關(guān)資料,需要的朋友可以參考下2017-03-03Android使用PullToRefresh實(shí)現(xiàn)上拉加載和下拉刷新效果的代碼
這篇文章主要介紹了Android使用PullToRefresh實(shí)現(xiàn)上拉加載和下拉刷新效果 的相關(guān)資料,需要的朋友可以參考下2016-07-07