Kotlin中局部方法的深入探究
前言
Kotlin是由開發(fā)過IntelliJ IDEA、Android Studio、PyCharm等IDE的著名IDE廠商JetBrains公司設(shè)計(jì)并開源的編程語言。2011年7月推出的Kotlin項(xiàng)目深受《Effective Java》的影響,直到2016年2月15日第一個(gè)官方穩(wěn)定版本Kotlin v1.0才正式發(fā)布,2017年Google I/O開發(fā)者大會(huì)中,Google宣布Kotlin成為Android開發(fā)的一級(jí)語言,Kotlin “轉(zhuǎn)正”。
在Kotlin中,定義方法很有趣,不僅僅因?yàn)榉椒ǖ年P(guān)鍵字是fun(function前幾個(gè)字符),還是因?yàn)槟銜?huì)驚奇的發(fā)現(xiàn),它允許我們在方法中定義方法。如下
fun methodA() {
fun methodB() {
}
methodB() //valid
}
//methodB() invalid
其中
- methodB定義在methodA的方法體中,即methodB被稱為局部方法或局部函數(shù)
- methodB只能在methodA中方法調(diào)用
- methodB在methodA方法外調(diào)用,會(huì)引起編譯錯(cuò)誤
既然Kotlin支持了局部方法,相比它應(yīng)該有什么特殊的用武之地呢
首先它的特點(diǎn)還是像它的名字一樣,局部,這就意味著它有著無可比擬的更小范圍的限定能力。保證了小范圍的可用性,隔絕了潛在的不相關(guān)調(diào)用的可能。
作為編程中的金科玉律,方法越小越好,相比縱向冗長的代碼片段,將其按照職責(zé)切分成功能單一的小的局部方法,最后組織起來調(diào)用,會(huì)讓我們的代碼顯得更加的有條理和清晰。
作為一個(gè)程序員,好奇應(yīng)該是他的特質(zhì)之一,我們應(yīng)該會(huì)想要研究一下,局部方法的實(shí)現(xiàn)原理是什么,至少我們在Java時(shí)代從來沒有見過這種概念。
其實(shí)這件事仔細(xì)研究起來,還是有不少細(xì)節(jié)的。因?yàn)檫@其中局部方法可以捕獲外部的變量也可以不捕獲外部的變量。
下面就是捕獲外部變量的一種情況
fun outMethodCapture(args: Array<String>) {
fun checkArgs() {
if (args.isEmpty()) {
println("innerMethod check args")
Throwable().printStackTrace()
}
}
checkArgs()
}
這其中,局部方法checkArgs捕獲了outMethodCapture的參數(shù)args。
所以,不捕獲外部變量的情況也不難理解,如下,即checkArgs處理args都是通過參數(shù)傳遞的。
fun outMethodNonCapture(args: Array<String>) {
fun checkArgs(args: Array<String>) {
if (args.isEmpty()) {
println("outMethodNonCapture check args")
Throwable().printStackTrace()
}
}
checkArgs(args)
}
首先我們分析一下捕獲變量的局部方法的實(shí)現(xiàn)原理
public static final void outMethodCapture(@NotNull final String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
<undefinedtype> checkArgs$ = new Function0() {
// $FF: synthetic method
// $FF: bridge method
public Object invoke() {
this.invoke();
return Unit.INSTANCE;
}
public final void invoke() {
Object[] var1 = (Object[])args;
if(var1.length == 0) {
String var2 = "innerMethod check args";
System.out.println(var2);
(new Throwable()).printStackTrace();
}
}
};
checkArgs$.invoke();
}
如上實(shí)現(xiàn)原理,就是局部方法實(shí)現(xiàn)其實(shí)就是實(shí)現(xiàn)了一個(gè)匿名內(nèi)部類的實(shí)例,然后再次調(diào)用即可。 對(duì)于不捕獲的局部方法要稍有不同,首先我們反編譯得到對(duì)應(yīng)的Java代碼
public static final void outMethodNonCapture(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
<undefinedtype> checkArgs$ = null.INSTANCE;
checkArgs$.invoke(args);
}
我們得到的是一個(gè)不完整的代碼,這時(shí)候需要我們前往項(xiàng)目工程,結(jié)合一些對(duì)應(yīng)的class文件分析。首先我們找到類似這樣的文件MainKt$outMethodCapture$1.class(其class文件按照”文件名$方法名$內(nèi)部類序號(hào)”的規(guī)則)。
使用javap方法再次反編譯分析該文件,注意對(duì)于$符號(hào)需要簡單處理一下。
➜ KotlinInnerFunction javap -c "MainKt\$outMethodNonCapture\$1.class"
Compiled from "Main.kt"
final class MainKt$outMethodNonCapture$1 extends kotlin.jvm.internal.Lambda implements kotlin.jvm.functions.Function1<java.lang.String[], kotlin.Unit> {
public static final MainKt$outMethodNonCapture$1 INSTANCE;
public java.lang.Object invoke(java.lang.Object);
Code:
0: aload_0
1: aload_1
2: checkcast #11 // class "[Ljava/lang/String;"
5: invokevirtual #14 // Method invoke:([Ljava/lang/String;)V
8: getstatic #20 // Field kotlin/Unit.INSTANCE:Lkotlin/Unit;
11: areturn
public final void invoke(java.lang.String[]);
Code:
0: aload_1
1: ldc #23 // String args
3: invokestatic #29 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
6: aload_1
7: checkcast #31 // class "[Ljava/lang/Object;"
10: astore_2
11: aload_2
12: arraylength
13: ifne 20
16: iconst_1
17: goto 21
20: iconst_0
21: ifeq 44
24: ldc #33 // String outMethodNonCapture check args
26: astore_2
27: getstatic #39 // Field java/lang/System.out:Ljava/io/PrintStream;
30: aload_2
31: invokevirtual #45 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
34: new #47 // class java/lang/Throwable
37: dup
38: invokespecial #51 // Method java/lang/Throwable."<init>":()V
41: invokevirtual #54 // Method java/lang/Throwable.printStackTrace:()V
44: return
MainKt$outMethodNonCapture$1();
Code:
0: aload_0
1: iconst_1
2: invokespecial #61 // Method kotlin/jvm/internal/Lambda."<init>":(I)V
5: return
static {};
Code:
0: new #2 // class MainKt$outMethodNonCapture$1
3: dup
4: invokespecial #80 // Method "<init>":()V
7: putstatic #82 // Field INSTANCE:LMainKt$outMethodNonCapture$1;
10: return
}
上面的類其實(shí)比較簡單,更重要的這是一個(gè)單例的實(shí)現(xiàn)。因?yàn)檫@樣相比捕獲的情況下,減少了匿名內(nèi)部類的生成和實(shí)例的創(chuàng)建,理論上帶來的代價(jià)也會(huì)更小。
考慮到上面的對(duì)比,如果在使用局部方法時(shí),建議使用不捕獲外部變量的方式會(huì)更加推薦。
使用注意
是的,使用局部方法有一個(gè)注意事項(xiàng),也就是一種規(guī)則約定,那就是需要先定義才能使用,否則會(huì)報(bào)錯(cuò),如下所示
fun outMethodInvalidCase(args: Array<String>) {
checkArgs()//invalid unresolved reference
fun checkArgs() {
if (args.isEmpty()) {
println("innerMethod check args")
Throwable().printStackTrace()
}
}
checkArgs()//valid
}
但是呢,先定義局部方法,再使用還是有一些問題,這種問題主要表現(xiàn)在代碼可讀性上。
試想一下,如果你進(jìn)入一個(gè)方法,看到的是一連串的局部方法,可能或多或少有點(diǎn)別扭。
但是試想一下,既然有這樣的問題,為什么還要被設(shè)計(jì)成這個(gè)樣子呢。首先,我們先看個(gè)小例子
0fun outMethodInvalidCase(args: Array<String>) {
checkArgs(args)
var a = 0 //the reason why it's unresolved
fun checkArgs(args: Array<String>) {
if (args.isEmpty()) {
println("outMethodNonCapture check args")
Throwable().printStackTrace()
a.toString()
}
}
}
因?yàn)榫植糠椒梢詂apture局部變量,checkArgs捕獲了局部變量a,當(dāng)?shù)谝恍写acheckArgs調(diào)用時(shí),而checkArgs看似定義了,但是第二行卻還沒有執(zhí)行到,導(dǎo)致了編譯問題。
目前,capture變量和非capture的局部方法使用都是一致的,都需要先定義,再使用。
關(guān)于Kotlin中的局部方法,我們可以去嘗試來達(dá)到限定范圍,拆分方法的目的,在使用時(shí),盡量選擇非捕獲的形式的局部方法。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
Android原生側(cè)滑控件DrawerLayout使用方法詳解
這篇文章主要為大家詳細(xì)介紹了Android原生側(cè)滑控件DrawerLayout的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
Android開發(fā)之HTTP訪問網(wǎng)絡(luò)
這篇文章主要介紹了Android開發(fā)之HTTP訪問網(wǎng)絡(luò)的相關(guān)資料,需要的朋友可以參考下2016-07-07
android TextView中識(shí)別多個(gè)url并分別點(diǎn)擊跳轉(zhuǎn)方法詳解
在本篇文章里小編給大家整理的是關(guān)于android TextView中識(shí)別多個(gè)url并分別點(diǎn)擊跳轉(zhuǎn)方法詳解,需要的朋友們可以學(xué)習(xí)參考下。2019-08-08
Android解決dialog彈出時(shí)無法捕捉Activity的back事件的方法
這篇文章主要介紹了Android解決dialog彈出時(shí)無法捕捉Activity的back事件的方法,涉及Android操作Activity事件的相關(guān)技巧,需要的朋友可以參考下2015-05-05

