天天看點

kotlin t class.java_Kotlin 調用Java寫的方法,參數Class<T> 神坑

看調用問題

先來看幾段代碼

Java 寫的 loadResource 方法,參數需要傳入 Class

public T loadResource(String resource, CCMMPParse parse, Class clazz){

HJKitConfigAssembledResourceModel model = new HJKitConfigAssembledResourceModel();

model.setContent(resource);

T result = parse.parse(model);

if (clazz.isInstance(result)){

return clazz.cast(result);

}

return null;

}

Kotlin 寫的 Parse 實作

class EmoticonMMPConfigParser : CCMMPParse {

override fun parse(model: BaseAssembledResourceModel?): Boolean {

return false

}

}

kotlin 嘗試調用 Java 寫的 loadResource 方法

val result = CCMMPManager.loadResource("true", EmoticonMMPConfigParser(), Boolean::class.java)

此時大家,大膽的猜測一下 result 運作結果是什麼?

result = null

是的你沒看錯,運作結果并不是我們 Parse 裡面傳回的 false,而是傳回了 null。 為什麼啊???

單獨跟蹤一下,會發現問題出在這裡:

if (clazz.isInstance(result)){

//這裡在判斷類型的時候,出錯了的,并沒有走到這個case中

return clazz.cast(result);

}

//走到這裡了的,是以傳回 null

return null;

那問題來了的,為什麼 clazz.isInstance(result) 判斷的結果是 false 呢,我們調用的是時候明明傳入的Class是Kotlin的 Boolean::class.java, 解析的 parse 也是用 Kotlin 寫的方法,傳回的也是 Boolean啊, 輸入輸出的應該是同一個類型,為什麼判斷結果卻是告訴我們不是一個類型呢???

一開始懷疑是,kotlin 和 Java 互相調用的時候,之間發生了什麼未知的坑,好那麼試試純用kotlin 寫一代嗎驗證一下。

init {

val b = getB()

Log.d("tag",".isInstance: ${ Boolean::class.java.isInstance(b)}")

}

fun getB():Boolean {

return true

}

你猜結果如何???

isInstance: false

Why ???

即 換成 javaObjectType.isInstance() :

Log.d("tag","isInstance: ${ Boolean::class.java.isInstance(b)}")

Log.d("tag","isInstance: ${ Boolean::class.javaObjectType.isInstance(b)}")

你猜結果如何???

isInstance: false

isInstance: true

parse 也換換?

val result = CCMMPManager.loadResource("true", EmoticonMMPConfigParser(), Boolean::class.javaObjectType)

result 運作結果是什麼?

result = false

Good.

好,現在來分析一下,問題到底出在哪裡?

首先來羅列一下代碼

Boolean::class.java 指向的是 kotlin 标準庫裡面定義的Boolean.kt類:

kotlin t class.java_Kotlin 調用Java寫的方法,參數Class<T> 神坑

Boolean.kt

Boolean::class.javaObjectType 指向的是 kotlin 标準庫中 JvmClassMappingKt 中的一個方法:

kotlin t class.java_Kotlin 調用Java寫的方法,參數Class<T> 神坑

javaObjectType

傳回的是JavaLangBoolean::class.java, 即JDK中的 Boolean.java 類:

kotlin t class.java_Kotlin 調用Java寫的方法,參數Class<T> 神坑

Boolean.java

而我們用 kotlin 寫的parse,在運作時傳回的 false是JVM給我們的基本類型(你可以了解是拆箱之後的數值),也就是對應的是 JavaLangBoolean::class.java, 而非我們以為的kotlin中寫的 Boolean.kt。

延伸 '編譯階段的文法糖' 拆箱和裝箱

The issue is that Int::class refers to the primitive int type, but the value is the boxed Integer type.

A workaround is to use Integer::class instead of Int::class, as this will refer to the boxed Java type.

寫在最後

我對JVM,其實沒有研究過,雖然以前上學的時候學過的,但可惜的是早就忘在腦後了的,是以如果哪裡寫的不對的,請見諒并希望指出來告訴我。

寫這篇文章,隻是為了記錄這個神坑。并且加深了之前自己的了解(也許是錯誤的),一切基于JVM的語言,最終都逃不過JVM的規範,換言之,你也可以創造一門新的JVM語言。

聊到這裡,不知道大家有沒有同樣的感受。我們的圈子裡面有兩個關鍵字:

語言: 到目前為止,有那麼多語言,每個語言都在不同平台上展示着自己的優勢。

工程師: 擅長不同的語言,在不同平台上去編碼。

是以,樓主堅信語言僅僅是工程師的一把工具而已,用來在不同的平台上砍瓜切菜。樓主個人認為,你我都有學不完的語言,也很難做到拍着胸脯說自己完全掌握了什麼什麼語言,但作為拿工具的我們,可以做到的是,掌握拿工具的姿勢,在不同平台環境下隻是選擇更加合适的工具,去拿刀(Swift)還是拿斧頭(Kotlin)而已。語言是用來駕馭的,千萬不要被語言牽着鼻子走。(是以前言裡面說的 這篇文章适合已經學過其中一門語言,不僅僅局限與Swift & Kotlin)