kotlin入門潛修之進(jìn)階篇—inline方法及其原理

本文收錄于 kotlin入門潛修專題系列什燕,歡迎學(xué)習(xí)交流。

創(chuàng)作不易竞端,如有轉(zhuǎn)載屎即,還請(qǐng)備注。

內(nèi)聯(lián)方法

在學(xué)習(xí)c/c++語言的時(shí)候事富,會(huì)了解到inline(內(nèi)聯(lián))方法技俐。java中并沒有inline方法,而kotlin提供了該功能统台,這是有別于java的一個(gè)地方雕擂。kotlin中使用inline關(guān)鍵字來修飾內(nèi)聯(lián)方法。

什么是inline方法贱勃?使用inline修飾的方法和普通方法有什么區(qū)別井赌?這背后的原理是什么谤逼?這就是本篇文章要闡述的內(nèi)容。

先來看下沒有使用inline修飾的方法仇穗,如下所示:

class Test {
//定義了一個(gè)普通的方法m1流部,該方法打印語句hello world
    fun m1() {
        println("hello world")
    }
//測(cè)試方法test,僅僅調(diào)用m1
    fun test() {
        m1()
    }
}

上面代碼就是一個(gè)普通的代碼調(diào)用纹坐,來看看其背后的字節(jié)碼實(shí)現(xiàn)(這里主要來看下test方法的實(shí)現(xiàn)):

  public final test()V
   L0
    LINENUMBER 13 L0
    ALOAD 0
//注意這里枝冀,編譯器會(huì)通過字節(jié)碼指令I(lǐng)NVOKEVIRTUAL
//來完成方法m1的調(diào)用
    INVOKEVIRTUAL Test.m1 ()V
   L1
    LINENUMBER 15 L1
    RETURN
   L2
    LOCALVARIABLE this LTest; L0 L2 0
    MAXSTACK = 1
    MAXLOCALS = 1

上面注釋已經(jīng)說明了普通方法的調(diào)用流程,即會(huì)通過字節(jié)碼指令完成方法的調(diào)用耘子,那么inline方法會(huì)有什么不同呢果漾?看個(gè)inline方法的示例:

class Test {
//此時(shí)方法m1就是內(nèi)聯(lián)方法,使用了inline關(guān)鍵字修飾
    inline fun m1() {
        println("hello world")
    }
//測(cè)試方法
    fun test() {
        m1()
    }
}

上述代碼僅僅將方法m1使用了inline關(guān)鍵字來修飾谷誓,其他什么都沒有變绒障,來看看其背后對(duì)應(yīng)的字節(jié)碼:

  public final test()V
   L0
    LINENUMBER 13 L0
    ALOAD 0
    ASTORE 1
   L1
    LINENUMBER 101 L1
    LDC "hello world"http://注意這里
    ASTORE 2
   L2
//同樣也注意這里
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 2
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
   L3
   L4
    LINENUMBER 102 L4
    NOP
  //省略部分字節(jié)碼

從字節(jié)碼可以發(fā)現(xiàn),inline方法被直接編譯到了調(diào)用處片林,是作為調(diào)用方法的一部分來實(shí)現(xiàn)的端盆,而不是通過方法調(diào)用來完成的怀骤,這就是inline方法和普通方法的區(qū)別费封!

那么,為什么要這么做呢蒋伦?答案是顯而易見的弓摘,因?yàn)榉椒ㄕ{(diào)用是有性能開銷的,而inline方法剛好可以將方法調(diào)用編譯到自己的方法體實(shí)現(xiàn)中痕届,故節(jié)省了很多開銷韧献。

如果,你覺得方法開銷不必在意的話研叫,那么就來看下面一段代碼:

class Test {
//方法m0接收了一個(gè)方法類型作為參數(shù)
    fun m0(checkStr: (str: String) -> String) {
        val str1 = "test str"
        println(checkStr(str1))
    }
    fun test() {
        m0({ "test2" })
    }
}

如果看過kotlin入門潛修之進(jìn)階篇—高階方法和lambda表達(dá)式
這篇文章锤窑,一定對(duì)上面的代碼不陌生,這就是kotlin中的高階方法嚷炉!在kotlin入門潛修之進(jìn)階篇—高階方法和lambda表達(dá)式原理
這篇文章中渊啰,我們?cè)治鲞^,方法類型實(shí)際上最終都是以對(duì)象的形式存在的申屹,kotlin會(huì)為lambda表達(dá)式生成一個(gè)新類绘证,并通過該類的實(shí)例完成方法的入?yún)⒓皥?zhí)行。所以這中間存在著一些內(nèi)存方面的開銷哗讥,如果有很多這種語句嚷那,那么這個(gè)開銷將會(huì)變的非常之大。

下面我們將m0方法改成inline方法進(jìn)行實(shí)現(xiàn)杆煞,源代碼如下所示:

class Test {
//m0方法使用了inline關(guān)鍵字來修飾魏宽,表示m0是個(gè)內(nèi)聯(lián)方法
    inline fun m0(checkStr: (str: String) -> String) {
        val str1 = "test str"
        println(checkStr(str1))
    }
    fun test() {
        m0({ "test2" })
    }
}

上面代碼將m0標(biāo)注為了inline方法腐泻,來看下其背后對(duì)應(yīng)的字節(jié)碼,如下所示:

// ================Test.class =================
// class version 50.0 (50)
// access flags 0x31
public final class Test {
  // access flags 0x11
  // signature (Lkotlin/jvm/functions/Function1<-Ljava/lang/String;Ljava/lang/String;>;)V
  // declaration: void m0(kotlin.jvm.functions.Function1<? super java.lang.String, java.lang.String>)
  public final m0(Lkotlin/jvm/functions/Function1;)V
    @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
   L0
    ALOAD 1
    LDC "checkStr"
    INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
   L1
    LINENUMBER 3 L1
    LDC "test str"
    ASTORE 3
   L2
    LINENUMBER 4 L2
    ALOAD 1
    ALOAD 3
    INVOKEINTERFACE kotlin/jvm/functions/Function1.invoke (Ljava/lang/Object;)Ljava/lang/Object;
    ASTORE 4
   L3
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 4
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
   L4
   L5
    LINENUMBER 5 L5
    RETURN
   L6
    LOCALVARIABLE str1 Ljava/lang/String; L2 L6 3
    LOCALVARIABLE this LTest; L0 L6 0
    LOCALVARIABLE checkStr Lkotlin/jvm/functions/Function1; L0 L6 1
    LOCALVARIABLE $i$f$m0 I L0 L6 2
    MAXSTACK = 2
    MAXLOCALS = 5

  // access flags 0x11
  public final test()V
   L0
    LINENUMBER 8 L0
    ALOAD 0
    ASTORE 1
   L1
    LINENUMBER 95 L1
    LDC "test str"
    ASTORE 2
   L2
    LINENUMBER 96 L2
    ALOAD 2
    ASTORE 3
   L3
    LINENUMBER 8 L3
    LDC "test2"
   L4
   L5
    ASTORE 3
   L6
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 3
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
   L7
   L8
    LINENUMBER 97 L8
    NOP
   L9
    LINENUMBER 9 L9
    RETURN
   L10
    LOCALVARIABLE it Ljava/lang/String; L3 L5 3
    LOCALVARIABLE $i$a$1$m0 I L3 L5 4
    LOCALVARIABLE str1$iv Ljava/lang/String; L2 L9 2
    LOCALVARIABLE this_$iv LTest; L1 L9 1
    LOCALVARIABLE $i$f$m0 I L1 L9 5
    LOCALVARIABLE this LTest; L0 L10 0
    MAXSTACK = 2
    MAXLOCALS = 6

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 1 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this LTest; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1
}

沒錯(cuò)湖员,上面就是生成的全部字節(jié)碼贫悄!通過字節(jié)碼可以發(fā)現(xiàn)以下兩點(diǎn):

  1. kotlin不再為lambda表達(dá)式生成一個(gè)新類。
  2. m0方法的實(shí)現(xiàn)會(huì)被編譯到其調(diào)用處(即test方法)中娘摔。

由此可見窄坦,inline方法會(huì)節(jié)省掉使用lambda或者匿名方法時(shí)所帶來的內(nèi)存開銷。這就是inline方法背后的優(yōu)勢(shì)凳寺。

noinline

當(dāng)inline作用于方法時(shí)鸭津,會(huì)同時(shí)對(duì)方法本身以及傳入的lambda起作用,換句話說肠缨,inline方法和lambda都會(huì)被編譯到方法的調(diào)用處(可以見上例)逆趋,那么如果我們只需要一部分方法被內(nèi)聯(lián)該如果做呢?這就是noinline關(guān)鍵字的作用晒奕!使用noinline關(guān)鍵字表明其修飾的部分不需要內(nèi)聯(lián)到調(diào)用處闻书,如下所示:

//代碼同上個(gè)例子基本一致
class Test {
//唯一不一樣的地方就是我們使用noinline修飾了m0方法
//的類型入?yún)?    inline fun m0(noinline checkStr: (str: String) -> String) {
        val str1 = "test str"
        println(checkStr(str1))
    }
//測(cè)試方法
    fun test() {
        m0({ "test2" })
    }
}

那么這么寫以后,kotlin會(huì)怎么處理呢脑慧?通過查看字節(jié)碼可知魄眉,kotlin會(huì)忽略加在方法開頭的inline修飾符,而照例為傳入的lambda表達(dá)式生成了一個(gè)新類闷袒!但是m0方法體中的實(shí)現(xiàn)卻被內(nèi)聯(lián)到了test方法中坑律,字節(jié)碼摘錄如下:

//test方法對(duì)應(yīng)的字節(jié)碼
  public final test()V
   L0
    LINENUMBER 8 L0
    ALOAD 0
    ASTORE 1
    GETSTATIC Test$test$1.INSTANCE : LTest$test$1;
    CHECKCAST kotlin/jvm/functions/Function1
    ASTORE 2
   L1
    LINENUMBER 95 L1
    LDC "test str"http://由此可知,m0方法體中代碼被內(nèi)聯(lián)到了此處
    ASTORE 3
   L2
    LINENUMBER 96 L2
    ALOAD 2
    ALOAD 3
//這里囊骤,可以看出晃择,是通過方法調(diào)用來完成lambda表達(dá)式功能的
    INVOKEINTERFACE kotlin/jvm/functions/Function1.invoke (Ljava/lang/Object;)Ljava/lang/Object;
    ASTORE 4
   L3
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 4
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
   L4
//下面是kotlin為lambda表達(dá)式生成的新類
final class Test$test$1 extends kotlin/jvm/internal/Lambda  implements kotlin/jvm/functions/Function1  {
//省略了類中的內(nèi)容
}

非局部返回(Non-local returns)

先來看個(gè)例子:

//定義了一個(gè)高階方法m0,該方法接收一個(gè)方法類型
    fun m0(param: () -> Unit) {
    }
//測(cè)試方法
    fun test() {
//調(diào)用m0方法
        m0 {
            return// !!!錯(cuò)誤也物,編譯不通過!
        }
    }

上述代碼將會(huì)編譯不通過宫屠!原因是return一般會(huì)結(jié)束方法,表示方法執(zhí)行完成滑蚯,而lambda表達(dá)式則無法結(jié)束方法浪蹂,也就是不能在lambda表達(dá)式中使用return語句。那么如果我們想要結(jié)束lambda的執(zhí)行該如何做呢膘魄?那就是使用label機(jī)制乌逐,如下所示:

    fun test() {
        m0 {
            return@m0//通過隱式的label返回
        }
    }

隱式的label就是不給m0指定label標(biāo)識(shí),而是通過默認(rèn)的方法名m0進(jìn)行返回创葡。需要注意浙踢,上面的return@m0是一個(gè)整體,不能有任何空格灿渴。使用label后就可以從lambda中返回洛波,然后繼續(xù)執(zhí)行方法體下面的語句胰舆。

那么如果不使用label還有什么辦法嗎?有蹬挤,使用inline方法即可缚窿,如下所示:

//將m0標(biāo)識(shí)為了inline方法
    inline fun m0(param: () -> Unit) {
    }
//測(cè)試方法
    fun test() {
        m0 {
            return//正確!
        }
    }

為什么inline方法可以運(yùn)行return呢焰扳?這是正是因?yàn)閕nline方法是會(huì)被編譯到調(diào)用處的方法體中倦零,所以可以使用return。但這也同時(shí)意味著吨悍,return語句會(huì)直接結(jié)束掉整個(gè)方法的執(zhí)行扫茅,而不會(huì)再執(zhí)行后面的語句。

這也是使用return@label和使用inline return的區(qū)別:前者僅僅是從當(dāng)前l(fā)abel作用域返回育瓜,后者則會(huì)返回整個(gè)方法葫隙。

使用inline方法返回的這種形式,就被稱為非局部返回躏仇。其定義為:位于lambda表達(dá)式內(nèi)恋脚,但是可以通過return直接結(jié)束其所屬方法的執(zhí)行。

inline修飾的方法都可以用作非局部返回焰手。kotlin標(biāo)準(zhǔn)庫中有很多inline方法糟描,如最常用的forEach方法。這些方法都可以用在非局部返回場(chǎng)景中册倒。

//kotlin庫定義的forEach方法蚓挤,是個(gè)inline方法
public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit {
    for (element in this) action(element)
}

舉個(gè)forEach非局部返回的使用場(chǎng)景:比如我們需要實(shí)現(xiàn)遇到偶數(shù)就結(jié)束方法運(yùn)行的功能磺送,如下所示:

    fun test() {
        listOf(1, 2, 3).forEach {
            if (it % 2 == 0) {//當(dāng)遇到偶數(shù)時(shí)就直接返回
                println(it)
                return
            }
            println(it)
        }
    }

上面代碼會(huì)打印1 2驻子。即當(dāng)遇到偶數(shù)時(shí)就直接返回了整個(gè)方法。如果forEach不是inline方法估灿,則無法這么使用崇呵。

crossinline

來看一個(gè)例子:

//定義了一個(gè)inline高階方法m0,接收一個(gè)方法類型作為入?yún)?    inline fun m0(checkStr: () -> Unit) {
        object : Runnable {
            override fun run() {
                checkStr()//!!!編譯錯(cuò)誤
            }
        }.run()
    }

上面代碼的場(chǎng)景是這樣的:我們不是在inline方法體中直接使用lambda表達(dá)式馅袁,而是在其方法體內(nèi)部的匿名對(duì)象中使用了lambda表達(dá)式域慷。這種場(chǎng)景編譯器會(huì)報(bào)錯(cuò),不允許這么做汗销!其實(shí)想想也是有道理的犹褒,因?yàn)槿绻试ScheckStr非局部返回的話,那么就會(huì)直接結(jié)束整個(gè)run方法弛针,這可能會(huì)帶來難以預(yù)料的錯(cuò)誤叠骑。針對(duì)這種情況kotlin提供了一個(gè)關(guān)鍵字crossinline,使用crossinline修飾的lambda表達(dá)式可以應(yīng)用于方法體的任何地方削茁,如下所示:

    inline fun m0(crossinline  checkStr: () -> Unit) {
        object : Runnable {
            override fun run() {
//由于checkStr被使用了crossinline關(guān)鍵字修飾
//所以可以這么用
                checkStr()
                println("end")
            }
        }.run()
    }

具體化類型參數(shù)(Reified type parameters)

kotlin中也有反射宙枷,kotlin允許使用反射來訪問類型信息掉房,示例如下:

//根據(jù)類型獲取對(duì)應(yīng)的值,如果匹配我們的類型則返回對(duì)應(yīng)類型的默認(rèn)值
fun <T> getIntDefValByType(clazz: Class<T>): T? {
    val defVal = 0//Int的默認(rèn)值為0
    if (clazz.isInstance(defVal)) {//如果傳入的是Int則返回defVal
        return defVal as T?
    }
    return null//如果不是Int則返回null
}
//測(cè)試方法main
fun main(args: Array<String>) {
    println(getIntDefValByType(Int::class.javaObjectType))//打印 0
    println(getIntDefValByType(String::class.javaObjectType))//打印null
}

上面方法的功能是根據(jù)獲取Int類型的默認(rèn)值慰丛,如果是類型匹配則返回其默認(rèn)值0卓囚,否則返回null。::class表示獲取kotlin中的類型信息诅病,而::class.javaObjectType表示獲取java對(duì)應(yīng)的類型信息(也可以使用::class.java哪亿,但是::class.java無法返回包裹類型對(duì)應(yīng)的類型信息,而javaObjectType可以)贤笆,這個(gè)方法不具有實(shí)際意義锣夹,但是說明kotlin支持使用反射來進(jìn)行類型校驗(yàn)。

上述代碼雖然能完成功能苏潜,但是使用起來不太優(yōu)雅银萍,我們還要傳入具體的類型,那么有沒有更優(yōu)雅的使用方法恤左?當(dāng)然有贴唇,這就是使用reified關(guān)鍵字。示例如下:

//方法需要使用inline修飾飞袋,加上了reified 關(guān)鍵字戳气,
//此時(shí)方法不需要Class類型的入?yún)?inline fun <reified T> getIntDefValByType2(): T? {
    val defVal = 0
    if (defVal is T) {//在這里判斷是否是T類型即可
        return defVal
    }
    return null
}
//測(cè)試方法main
fun main(args: Array<String>) {
    println(getIntDefValByType2<Int>())//打印 0
    println(getIntDefValByType2<String>())//打印 null
}

上面就是reified的用法,其對(duì)應(yīng)的調(diào)用形式就顯得相對(duì)優(yōu)雅些巧鸭。最后需要注意的是瓶您,reified關(guān)鍵字只能用于inline方法中!

那么reified背后的原理是什么纲仍?為什么我們使用reified就無法進(jìn)行類型判斷呀袱?

對(duì)于泛型,前面kotlin入門潛修之類和對(duì)象篇—泛型及其原理這篇文章已經(jīng)闡述的很詳細(xì):kotlin中的泛型同java一樣郑叠,都會(huì)在運(yùn)行期進(jìn)行類型擦除夜赵,所以我們使用"is T"這種方法來判斷類型的時(shí)候是不可能的,因?yàn)檫\(yùn)行時(shí)根本就不存在所謂的T類型乡革。那么reified關(guān)鍵字背后又做了什么寇僧?

我們來看下reified的背后的字節(jié)碼實(shí)現(xiàn),首先先把源代碼粘貼出來沸版,如下所示:

//要分析的源代碼
inline fun <reified T> getIntDefValByType2(): T? {
    val defVal = 0
    if (defVal is T) {
        return defVal
    }
    return null
}
//測(cè)試代碼
fun main(args: Array<String>) {
    getIntDefValByType2<Int>()//該語句符合getIntDefValByType2所需類型Int(defVal類型)
    getIntDefValByType2<String>()//該語句不符合getIntDefValByType2所需的類型Int(defVal類型)

在來看下其對(duì)應(yīng)的字節(jié)碼嘁傀,這里摘錄重點(diǎn)的一部分,如下所示:

//這里只摘錄了main方法中對(duì)應(yīng)的一部分代碼视粮,
    LINENUMBER 121 L9
    ILOAD 1
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    INSTANCEOF java/lang/String
    IFEQ L10

上面字節(jié)碼摘錄了main方法對(duì)應(yīng)的字節(jié)碼细办,其實(shí)main方法總共就兩句源代碼,一句是getIntDefValByType2<Int>()馒铃,一句是getIntDefValByType2<String>()蟹腾。因?yàn)間etIntDefValByType2方法是inline的痕惋,所以getIntDefValByType2的實(shí)現(xiàn)會(huì)被編譯到main方法當(dāng)中。但是通過字節(jié)碼發(fā)現(xiàn)娃殖,getIntDefValByType2<String>()語句會(huì)加上一句類型判斷值戳,即上面粘貼出來的字節(jié)碼:INSTANCEOF java/lang/String。而getIntDefValByType2<Int>卻沒有對(duì)應(yīng)的字節(jié)碼語句炉爆。這說明了什么堕虹?

是時(shí)候總結(jié)下reified背后的原理了。reified實(shí)際上是作用在編譯期間的芬首,由于reified必須用于inline方法中赴捞,而對(duì)于inline方法實(shí)際上是編譯到當(dāng)前代碼的調(diào)用處,所以在編譯的時(shí)候編譯器就能根據(jù)defVal來確認(rèn)其對(duì)應(yīng)的具體類型了郁稍。當(dāng)T被傳入多個(gè)類型時(shí)(比如Int赦政、String等等),kotlin就會(huì)在編譯的時(shí)候插入INSTANCEOF字節(jié)碼指令進(jìn)行類型判斷耀怜,INSTANCEOF指令會(huì)檢測(cè)是否是指定類的實(shí)例恢着,在本例中,如果是則返回defVal财破,否則返回null掰派。而在INSTANCEOF判斷之前,還有一句字節(jié)碼指令: INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
這句指令表示將Int的值轉(zhuǎn)為java下的Integer類型左痢,并獲取其值靡羡,這樣才有后面INSTANCEOF指令的判斷。

內(nèi)聯(lián)屬性(inline properties)

從kotlin1.1之后俊性,inline關(guān)鍵字可以用于沒有后備字段的屬性上略步,如下所示:

class Test {
    val t0: Int = 2//正確,普通的常量(變量定義)
    val t1: Int//正確磅废,可以使用inline修飾get
        inline get() = 1

    var t2: Int//正確
        inline get() = 1
        set(value) {}

    var t3: Int//正確
        inline get() = 3
        inline set(value) {}

    var t4: Int//正確
        get() = 1
        set(value) {}

    var t5: Int//!!!錯(cuò)誤纳像,這里顯示使用了field字段
        inline get() = 3
        inline set(value) {
            field = value
        }

    inline val t6 = 1//!!!錯(cuò)誤荆烈,這里默認(rèn)使用了field字段
    inline var t7 = 2//!!!錯(cuò)誤拯勉,這里默認(rèn)使用了field字段

    inline var t8: Int//!!!正確,我們復(fù)寫了get和set憔购,就沒有了field字段
        get() = 1
        set(value) {}
}

上述代碼比較容易理解宫峦,只需要記住只要有field字段就無法使用inline關(guān)鍵字即可。關(guān)于什么時(shí)候有field字段玫鸟,什么時(shí)候沒有导绷,可記住一句話:只有使用默認(rèn)的getter(setter)以及顯示使用field字段的時(shí)候,后備字段才會(huì)存在屎飘。具體可參見另一篇文章:kotlin入門潛修之類和對(duì)象篇—屬性和字段

那么inline屬性有什么作用妥曲?答案是顯而易見的:inline屬性同inline方法一樣會(huì)被編譯器編譯到其調(diào)用處贾费,避免了一些開銷。

公有內(nèi)聯(lián)方法的限制

使用inline方法會(huì)有一個(gè)潛在的問題檐盟,那就是當(dāng)一個(gè)模塊調(diào)用其他模塊的公有inline方法時(shí)褂萧,由于inline方法會(huì)被編譯到調(diào)用處,所以可能會(huì)存在其他模塊方法變更葵萎,而當(dāng)前模塊沒有重新編譯的問題导犹。這種問題主要是使用了非公有的inline方法引起的(即公有的inline方法調(diào)用了非公有的inline方法),所以kotlin就限制公有的inline方法不能調(diào)用非公有的inline方法羡忘。如下所示:

//私有inline方法
    private inline fun m0() {}
//默認(rèn)為public的共有inline方法
    inline fun m1() {}
//internal inline方法
    internal fun m2() {}
//使用@PublishedApi修飾的internal inline方法
    @PublishedApi
    internal fun m3() {}
//測(cè)試方法
    inline fun test() {
        m0()//錯(cuò)誤谎痢,public inline方法無法調(diào)用private inline方法
        m1()//正確,public inline方法可以調(diào)用public inline方法
        m2()//錯(cuò)誤卷雕,public inline方法無法調(diào)用internal inline方法
        m3()//正確节猿,public inline方法可以調(diào)用使用@PublishedApi注解標(biāo)注的internal inline方法,具體見下面解釋漫雕。
    }

這里所說的非公有的inline方法是指使用private和internal修飾的方法沐批。而對(duì)于internal修飾的方法,如果用戶使用了@PublishedApi注解進(jìn)行了標(biāo)識(shí)蝎亚,則可以被public inline方法使用九孩,因?yàn)槭褂聾PublishedApi注解標(biāo)識(shí)的方法,同public修飾的inline方法一樣,編譯器會(huì)在編譯的時(shí)候會(huì)進(jìn)行檢查发框。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末躺彬,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子梅惯,更是在濱河造成了極大的恐慌宪拥,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,561評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铣减,死亡現(xiàn)場(chǎng)離奇詭異她君,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)葫哗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門缔刹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人劣针,你說我怎么就攤上這事校镐。” “怎么了捺典?”我有些...
    開封第一講書人閱讀 157,162評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵鸟廓,是天一觀的道長。 經(jīng)常有香客問我,道長引谜,這世上最難降的妖魔是什么牍陌? 我笑而不...
    開封第一講書人閱讀 56,470評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮员咽,結(jié)果婚禮上呐赡,老公的妹妹穿的比我還像新娘。我一直安慰自己骏融,他們只是感情好链嘀,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著档玻,像睡著了一般怀泊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上误趴,一...
    開封第一講書人閱讀 49,806評(píng)論 1 290
  • 那天霹琼,我揣著相機(jī)與錄音,去河邊找鬼凉当。 笑死枣申,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的看杭。 我是一名探鬼主播忠藤,決...
    沈念sama閱讀 38,951評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼楼雹!你這毒婦竟也來了模孩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,712評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤贮缅,失蹤者是張志新(化名)和其女友劉穎榨咐,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谴供,經(jīng)...
    沈念sama閱讀 44,166評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡块茁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了桂肌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片数焊。...
    茶點(diǎn)故事閱讀 38,643評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖轴或,靈堂內(nèi)的尸體忽然破棺而出昌跌,到底是詐尸還是另有隱情,我是刑警寧澤照雁,帶...
    沈念sama閱讀 34,306評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響饺蚊,放射性物質(zhì)發(fā)生泄漏萍诱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評(píng)論 3 313
  • 文/蒙蒙 一污呼、第九天 我趴在偏房一處隱蔽的房頂上張望裕坊。 院中可真熱鬧,春花似錦燕酷、人聲如沸籍凝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饵蒂。三九已至,卻和暖如春酱讶,著一層夾襖步出監(jiān)牢的瞬間退盯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評(píng)論 1 266
  • 我被黑心中介騙來泰國打工泻肯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留渊迁,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,351評(píng)論 2 360
  • 正文 我出身青樓灶挟,卻偏偏與公主長得像琉朽,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子稚铣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容