1霉撵、匿名函數(shù)語法
匿名函數(shù)就是沒有名稱的函數(shù)浙于,它除了沒有名稱之外芹助,其他與具名函數(shù)是一樣的堂湖。
你們函數(shù)的語法如下:
fun([args]) [:returnType]{
[body]
}
args:就是函數(shù)參數(shù)列表,與具名函數(shù)的參數(shù)是一樣的規(guī)則
returnType:函數(shù)的返回類型状土,與具名函數(shù)的參數(shù)是一樣的規(guī)則
匿名函數(shù)屬于函數(shù)類型
2无蜂、匿名原理
你們函數(shù)的實現(xiàn)原理與kotlin—lambda及其原理差不多一樣的原理,它們都生成一個匿名類蒙谓,通過匿名類的來實現(xiàn)匿名函數(shù)的對應方法體斥季。
匿名函數(shù)也是根據(jù)函數(shù)參數(shù)個數(shù)實現(xiàn)Function系列的接口,我們通過例子來分析其原理:
舉例:
class TestClosure {
val b = 20
val sum : () -> Int = fun() : Int {
return b + 10
}
}
TestClosure 中聲明了一個匿名函數(shù)sum變量累驮,它使用外部TestClosure 類的成員變量b酣倾。
編譯TestClosure 之后,除了生成TestClosure.class之外谤专,還生成了TestClosure1.class躁锡,它是匿名函數(shù)的對應的匿名類,我們通過javac -c -v -p TestClosure
1.class反編譯看看其偽代碼:
final class com.wyx.tcanvas.test.delegate.TestClosure$sum$1 extends kotlin.jvm.internal.Lambda implements kotlin.jvm.functions.Function0<java.lang.Integer>
//省略.....
{
//聲明TestClosure對象置侍,即匿名類持有外部類的引用
final com.wyx.tcanvas.test.delegate.TestClosure this$0;
descriptor: Lcom/wyx/tcanvas/test/delegate/TestClosure;
flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
//匿名類構造函數(shù)映之,參數(shù)是外部類的引用 com.wyx.tcanvas.test.delegate.TestClosure$sum$1(com.wyx.tcanvas.test.delegate.TestClosure);
descriptor: (Lcom/wyx/tcanvas/test/delegate/TestClosure;)V
flags: (0x0000)
Code:
stack=2, locals=2, args_size=2
//0-3:將外部類引用賦值給this$0
0: aload_0
1: aload_1
2: putfield #13 // Field this$0:Lcom/wyx/tcanvas/test/delegate/TestClosure;
5: aload_0
6: iconst_0
7: invokespecial #16 // Method kotlin/jvm/internal/Lambda."<init>":(I)V
10: return
//省略......
//匿名函數(shù)的真實方法體
public final java.lang.Integer invoke();
descriptor: ()Ljava/lang/Integer;
flags: (0x0011) ACC_PUBLIC, ACC_FINAL
Code:
stack=2, locals=1, args_size=1
//0-13:調用this$0.getgetB() + 10,并返回蜡坊,其實就是匿名函數(shù)的b + 10
0: aload_0
1: getfield #13 // Field this$0:Lcom/wyx/tcanvas/test/delegate/TestClosure;
4: invokevirtual #28 // Method com/wyx/tcanvas/test/delegate/TestClosure.getB:()I
7: bipush 10
9: iadd
10: invokestatic #34 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
13: areturn
//省略......
//實現(xiàn)的Function接口invoke方法
public java.lang.Object invoke();
descriptor: ()Ljava/lang/Object;
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
//調用匿名函數(shù)的實體invoke方法并返回
0: aload_0
1: invokevirtual #37 // Method invoke:()Ljava/lang/Integer;
4: areturn
//省略.....
}
//省略.....
通過反編譯之后匿名類的偽代碼杠输,我們發(fā)現(xiàn)其實匿名函數(shù)與kotlin—lambda及其原理的原理是一樣的。
匿名函數(shù)會生成一個內部匿名類秕衙,匿名類繼承l(wèi)ambda類同時實現(xiàn)Function系列接口蠢甲,F(xiàn)unction接口的invoke方法調用匿名函數(shù)的真實實現(xiàn)函數(shù)invoke。
如果匿名函數(shù)沒有引用外部類的成員則使用匿名類單例對象的据忘;如果匿名函數(shù)使用了外部類的成員則內部會持有外部類的引用鹦牛,在匿名函數(shù)的實現(xiàn)函數(shù)invoke中通過持有外部類的引用來調用外部類的成員搞糕。
3、總結
匿名函數(shù)會生成一個內部匿名類能岩,匿名類繼承l(wèi)ambda類同時實現(xiàn)Function系列接口寞宫,F(xiàn)unction接口的invoke方法調用匿名函數(shù)的真實實現(xiàn)函數(shù)invoke。
如果匿名函數(shù)沒有引用外部類的成員則使用匿名類單例對象的方式拉鹃;如果匿名函數(shù)使用了外部類的成員則內部會持有外部類的引用辈赋,在匿名函數(shù)的實現(xiàn)函數(shù)invoke中通過持有外部類的引用來調用外部類的成員。
匿名函數(shù)與lambda一樣要注意不要濫用膏燕,因為它會生成額外的匿名類class文件钥屈,在使用時會創(chuàng)建匿名類實例對象,增加額外的空間和執(zhí)行時間損耗坝辫,如果只是用匿名函數(shù)代表一個函數(shù)并執(zhí)行此函數(shù)其實并非是明智的選擇篷就。
匿名函數(shù)與lambda還需要注意的是,如果引用了外部類的成員近忙,那么它們內部會持有外部類的引用竭业,如果使用不當會引起內存泄漏,比較典型的就是Android中的Activity使用匿名類導致的內存泄漏——解決方案是使用弱引用等