先來看一個(gè)概念哪工。
函數(shù)式接口:函數(shù)式接口(Functional Interface)就是一個(gè)有且僅有一個(gè)抽象方法弧哎,但是可以有多個(gè)非抽象方法的接口。
比如java中的Runnable接口就是函數(shù)式接口撤嫩。
public interface Runnable {
public abstract void run();
}
在項(xiàng)目代碼轉(zhuǎn)為使用kotlin時(shí),會(huì)存在和java代碼互相調(diào)用的問題鸭限。不知道大家有沒有注意到一個(gè)細(xì)節(jié),kotlin構(gòu)建java中的函數(shù)式接口時(shí)败京,可以轉(zhuǎn)為lambda方式。
java方式:
Runnable runnable = new Runnable() {
@Override
public void run() {
}
};
val runnable = Runnable {
print("runnable")
}
可以看到使用lambda表達(dá)式后朴皆,對(duì)函數(shù)式接口來講泛粹,實(shí)現(xiàn)方式更加簡潔。
我們都知道kotlin代碼最終還是會(huì)被編譯為字節(jié)碼晶姊,那么kotlin中是如何實(shí)現(xiàn)lambda表達(dá)式的呢?
這得分兩種情況:
情況1: lambda表達(dá)式內(nèi)部沒有持有外部的非靜態(tài)變量钾怔,方法蒙挑。
class Lambda{
private var name = "小明" // 外部變量
private fun testLambda(){
var age = 10 // 外部變量
val runnable = Runnable {
print("沒有調(diào)用外部變量或方法") // 沒有調(diào)用age或者name變量
}
}
}
查看testLambda
方法編譯后的字節(jié)碼
// 獲取靜態(tài)變量 INSTANCE(該INSTANCE實(shí)際是Runnable對(duì)象的一個(gè)實(shí)現(xiàn))
GETSTATIC Lambda$testLambda1$runnable$1.INSTANCE : LLambda$testLambda1$runnable$1;
// 檢查 INSTANCE 是否可以轉(zhuǎn)變?yōu)镽unnbale對(duì)象,否則拋出異常
CHECKCAST java/lang/Runnable
// INSTANCE推入操作數(shù)棧
ASTORE 1
翻譯成java代碼就會(huì)類似于這樣:
public class Lambda {
private String name = "小明";
private static Object INSTANCE = new Runnable() {
@Override
public void run() {
System.out.println("沒有調(diào)用外部類的成員變量");
}
};
private void testLambda1() {
Runnable runnable = (Runnable)INSTANCE;
}
}
可以看到這種情況下lambda表達(dá)式實(shí)際上被轉(zhuǎn)換為靜態(tài)成員變量矾利。
情況二:lambda表達(dá)式內(nèi)部持有外部的非靜態(tài)變量馋袜,方法。
class Lambda {
private var name = "小明"
private fun testLambda2() {
val runnable = Runnable {
print(name)
}
}
}
反編譯為java代碼:
private final void testLambda2() {
Runnable runnable = (Runnable)(new Runnable() {
public final void run() {
String var1 = Lambda.this.name;
System.out.print(var1);
}
});
}
可以看到lambda表達(dá)式是采用匿名內(nèi)部類的方式來實(shí)現(xiàn)的剑肯。
那么以上兩種方式有什么不同呢观堂?
我們都知道java中匿名內(nèi)部類都會(huì)隱式持有外部類的引用(即使不需要引用外部類的變量)让网,當(dāng)匿名內(nèi)部類中有耗時(shí)操作時(shí)师痕,容易造成內(nèi)存泄露。
像kotlin這樣的實(shí)現(xiàn)因篇,在情況一的時(shí)候,lambda內(nèi)部沒有持有外部類的引用竞滓,不會(huì)有任何內(nèi)存泄露的風(fēng)險(xiǎn)。
以上就是kotlin調(diào)用java時(shí)锯茄,lambda的實(shí)現(xiàn)方式茶没。
kotlin中Functions.kt
中定義了從Function0
到Function22
一共23個(gè)接口,每個(gè)接口有且僅有一個(gè)invoke
方法抓半,F(xiàn)unction后邊的數(shù)字代表invoke
方法有幾個(gè)入?yún)ⅲ?dāng)kotlin中自定義的lambda表達(dá)式在編譯的時(shí)候笛求,會(huì)被替換為對(duì)應(yīng)的接口實(shí)現(xiàn)。
比如:
val lambda: (() -> Unit) = {
}
由于沒有參數(shù)画机,所以實(shí)際上會(huì)被編譯為
Function0 lambda = new Function0{
public void invoke(){
}
}
當(dāng)然了新症,編譯規(guī)則也會(huì)像調(diào)用java中的函數(shù)式接口時(shí)一樣响禽,僅當(dāng)需要持有this
對(duì)象時(shí)才會(huì)編譯為匿名內(nèi)部類。