Kotlin學習筆記——接口的默認方法

2021年5月13日

JDK版本 1.8、Idea版本 2021.1售滤、Kotlin版本 1.4.10、使用maven構建項目

實踐出真知

概念

kotlin接口中的方法支持默認值,與Java8中提供的default關鍵字作用一致
例如我們定義一個Human接口:

interface Human {
    fun name() = "Human"
}

其中name()方法指定了默認值完箩,連default都不用寫赐俗,就可以讓接口方法具有默認返回值。

起因

剛開始學kotlin的時候弊知,我粗略的看了下阻逮,就沒放在心上,直到實際上手秩彤,才發(fā)現(xiàn)用Java的class實現(xiàn)kotlin接口的時候叔扼,默認方法還是要顯式實現(xiàn)的!

寫個MaleHuman實現(xiàn)該接口漫雷,發(fā)現(xiàn)IDEA報錯 ??

idea編輯器報錯

看來瓜富,name()仍是一個抽象方法,用kotlin代碼實現(xiàn)的默認方法對Java編譯器“不可見”降盹,自然需要在Java代碼中實現(xiàn)這個抽象方法啦与柑!

趕忙用IDEA自動生成實現(xiàn)方法:

public class MaleHuman implements Human{
    @NotNull
    @Override
    public String name() {
        return Human.super.name();
    }
}

OK,IDEA不報錯了澎现,跑個main方法先仅胞,然而。剑辫。干旧。編譯還是過不去 ??

build時報錯


探究

這時候想到,學習一門語言不僅要掌握語法結構妹蔽,還要了解代碼在經過編譯之后的執(zhí)行邏輯椎眯。

用jd-gui試著將字節(jié)碼反編譯成java代碼看下

雖然目前的反編譯工具對Kotlin字節(jié)碼不太友好,但是至少能看個大概

很明顯胳岂,Kotlin編譯器首先會為接口類生成一個DefaultImpls靜態(tài)內部類编整,這一點在編譯輸出目錄中也能看到

target目錄下的字節(jié)碼文件

然后將我們代碼中寫的方法體作為這個內部類的同名靜態(tài)方法的方法體,不過還要傳一個實現(xiàn)類的對象進去乳丰。

看到這里掌测,再結合其他大佬的文章[1][2],可以得出反編譯工具給出的“偽Java代碼”少掉的部分产园,就是name()方法被調用時汞斧,從抽象方法指向其內部類靜態(tài)方法的邏輯

進一步推測:從Java代碼中調用kotlin接口抽象方法時,編譯器(javac)“看到的”只是我們調用了一個abstract方法什燕,這個方法并沒有被default修飾奕筐,也沒有任何方法體鹦付,編譯器無法獲取更多的細節(jié)川蒙,干脆報錯著角,中斷編譯過程事富。

當然還是有很多似懂非懂的地方,比如為何在kotlin實現(xiàn)類里調用接口默認方法時可以通過編譯乘陪?JVM是如何處理kotlin接口的默認方法調用的统台?以及Kotlin為什么要采取這種方式生成字節(jié)碼?


簡單處理

這里就不再深挖了暂刘,既然知道kotlin編譯器會為接口的默認方法生成一個內部類饺谬,那我們拿來用就可以了

public class MaleHuman implements Human{
    @NotNull
    @Override
    public String name() {
        return Human.DefaultImpls.name(this);
    }
}

直接調用內部類DefaultImplsname()方法,傳入當前對象谣拣,然后編譯運行募寨,測試代碼如下:

public static void main(String[] args) {
    MaleHuman male = new MaleHuman();
    System.out.println(male.name());
}

成功輸出“Human”字符串。

通用方法

不過仔細想想森缠,每一個Java實現(xiàn)類都要寫這種模板代碼拔鹰,肯定不能滿足我們對于代碼整潔易維護的要求,還是查查有沒有更標準的方式吧

這時看到官方標準庫中提供了@JvmDefault注解贵涵,正是我們需要的列肢,馬上加到name()方法上試下,結果IDEA報錯

意思是這個注解必須在編譯時帶上-jvm-target 1.8這個參數(shù)宾茂,對應到maven工程就是需要在kotlin插件中添加jvmTarget參數(shù)

                <configuration>
                    <jvmTarget>1.8</jvmTarget>
                </configuration>

加上后再編譯瓷马,結果還是報錯。跨晴。

和剛才的一樣欧聘,都是少編譯選項,不同的是這次少了-Xjvm-default端盆,參考Stack Overflow上的答案[3]怀骤,還是在maven的kotlin插件中添加args參數(shù),附上完整版

                <configuration>
                    <jvmTarget>1.8</jvmTarget>
                    <args>
                        <arg>-Xjvm-default=enable</arg>
                    </args>
                </configuration>

(這里先設置成enable焕妙,下面再談原因)

加上后編譯不報錯了蒋伦,測試代碼也能夠正常輸出“Human”字符串
看下編譯后的代碼

帶了@JvmDefault注解

靜態(tài)內部類不見了,抽象方法不見了焚鹊,留給我們的只是單單一個的name()方法痕届,Java實現(xiàn)類也不需要顯式實現(xiàn)kotlin接口的默認方法了!并且IDEA自動生成的代碼也是支持的末患!

public class MaleHuman implements Human{
    @NotNull
    @Override
    public String name() {
        return Human.super.name();
    }
}


后續(xù)

問題就此解決爷抓,kotlin接口中的默認方法終于也能像Java接口中的default方法一樣被調用了,但是探尋的腳步還不能停阻塑,俗話說飲水思源,我們上kotlin官網看看@JvmDefault這個注解的文檔[4]

先感嘆下果复,現(xiàn)在kotlin版本都到1.5了陈莽,版本號刷得真勤啊

此時發(fā)現(xiàn)官方文檔中給這一頁寫了一個大大的Deprecated,讓我們不要再用這個注解了,仔細閱讀后覺得Kotlin開發(fā)團隊這樣做是有原因的走搁,之前@JvmDefault這個注解加上后独柑,根據(jù)傳遞給編譯器的參數(shù)不同,有兩種實現(xiàn)方式:

編譯器參數(shù) 作用
-Xjvm-default=enable 注解方法會編譯成default方法私植,內部類中的方法會被移除
-Xjvm-default=compatibility 注解方法會編譯成default方法忌栅,內部類中的方法仍會保留

以上是1.2.40版本時官方給出的解決方案,基于方法注解來處理曲稼。
那么問題來了索绪,如果一個接口中有多個默認方法呢?難道每個方法上都要加上這個注解才可以讓Java代碼正常調用嗎贫悄?

于是官方在1.4版本中調整了編譯器在處理接口默認方法時的作用范圍瑞驱,并給出了全新的編譯器參數(shù)

編譯器參數(shù) 作用
-Xjvm-default=all 所有接口的默認方法都會生成default方法且不會生成內部類
-Xjvm-default=all-compatibility 所有接口的默認方法都會生成default方法但仍會生成內部類
-Xjvm-default=compatibility all-compatibility作用一致,主要是為了兼容1.4版本之前的參數(shù)

還有一個新注解@JvmDefaultWithoutCompatibility窄坦,是用在類上的唤反,用來告訴編譯器當前類或者接口中的默認方法無需生成DefaultImpls內部類,應該是和上面的參數(shù)配合使用的鸭津。

新東西總要實踐一下彤侍,定義一個接口Something,有三個默認方法逆趋,一個抽象方法

interface Something {
    fun a() = "a"

    fun b() = true

    fun c() = 3

    fun d()
}

經過測試盏阶,新方式不再需要修改代碼,直接在打包編譯時指定參數(shù)即可父泳!

  • 當傳遞給編譯器-Xjvm-default=all參數(shù)時

三個方法都有了默認實現(xiàn)般哼,并且沒有生成內部類

  • 當傳遞給編譯器-Xjvm-default=all-compatibility參數(shù)時

三個默認方法既出現(xiàn)在接口中,也出現(xiàn)在內部類里惠窄,而且內部類中的方法還加上了@Deprecated注解


總結

  1. 避免在同一個工程中使用Java與Kotlin這兩種語言編寫代碼蒸眠。

盡管官方宣稱Kotlin提供了與Java的“一流”的互操作性,但是畢竟是兩種語言杆融,從文件楞卡、語法、結構到插件脾歇、編譯器蒋腮,甚至代碼運行時的行為都有差異,在開發(fā)過程中藕各,一定會不可避免地出現(xiàn)各種各樣的問題池摧,所以水平有限的話還是讓兩者(物理上)離得越遠越好;

  1. 如果實在避免不了激况,那就在模塊設計上避免作彤。

盡量只暴露方法或者API調用膘魄,而不是去繼承、實現(xiàn)對方的類或者接口竭讳,這樣可以將問題的發(fā)生場景最大限度地控制在可空性判斷上创葡。


參考文章

[1] 【Kotlin填坑-08】object 用法以及 Kotlin 的反編譯

[2] Kotlin JVM常用注解參數(shù)解析

[3] kotlin - @JvmDefault and how add compiler option - Stack Overflow

[4] JvmDefault - Kotlin Programming Language

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市绢慢,隨后出現(xiàn)的幾起案子灿渴,更是在濱河造成了極大的恐慌,老刑警劉巖胰舆,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件骚露,死亡現(xiàn)場離奇詭異,居然都是意外死亡思瘟,警方通過查閱死者的電腦和手機荸百,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來滨攻,“玉大人够话,你說我怎么就攤上這事」馊疲” “怎么了女嘲?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長诞帐。 經常有香客問我欣尼,道長,這世上最難降的妖魔是什么停蕉? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任愕鼓,我火速辦了婚禮,結果婚禮上慧起,老公的妹妹穿的比我還像新娘菇晃。我一直安慰自己,他們只是感情好蚓挤,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布磺送。 她就那樣靜靜地躺著,像睡著了一般灿意。 火紅的嫁衣襯著肌膚如雪估灿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天缤剧,我揣著相機與錄音馅袁,去河邊找鬼。 笑死荒辕,一個胖子當著我的面吹牛司顿,可吹牛的內容都是我干的芒粹。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼大溜,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了估脆?” 一聲冷哼從身側響起钦奋,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎疙赠,沒想到半個月后付材,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡圃阳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年厌衔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捍岳。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡富寿,死狀恐怖,靈堂內的尸體忽然破棺而出锣夹,到底是詐尸還是另有隱情页徐,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布银萍,位于F島的核電站变勇,受9級特大地震影響,放射性物質發(fā)生泄漏贴唇。R本人自食惡果不足惜搀绣,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望戳气。 院中可真熱鬧链患,春花似錦、人聲如沸物咳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽览闰。三九已至芯肤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間压鉴,已是汗流浹背崖咨。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留油吭,地道東北人击蹲。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓署拟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親歌豺。 傳聞我的和親對象是個殘疾皇子推穷,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內容