奇技淫巧之Kotlin 扩展函数(一)

关键字:Kotlin扩展(Extension)、inline,Kotlin反编译Java 源码

1. 范例

废话少说,先上范例,来看看扩展函数有什么用。

需求:

  1. 将任意对象转呼为json
  2. 在任意对象中添加打印日志方法,打印的日志使用类名做为TAG

实现代码
新建:Any+Extension.kt

val gson
    get() = Gson()

fun Any.log(msg: String) {
    Log.d("_" + this::class.java.simpleName, msg)
}

fun Any.toJson(): String {
    return gson.toJson(this)
}

测试代码

@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
    data class Person(val name: String, val age: Int, val gender: String, val interest: String)
    @Test
    fun extendTestCase() {1
        val person = Person("jack", 18, "男", "爱好女")
        //调用log 的对象是ExampleInstrumentedTest因此TAG 日志打印的TAG是\ExampleInstrumentedTest
        log(person.toJson())
    }
}

2. java 实现

如果使用java 实现上面打印日志和toJson的需求的方式就是创建一个工具类

public class Utils {

    public static final void log(Object target, String msg) {
        Log.d(target.getClass().getSimpleName(), msg);
    }

    public static final String toJson(Object target) {
        return new Gson().toJson(target);
    }
}

3. Kotlin 扩展函数原理

通过范例,我们已经知道Kotlin扩展函数怎么用了,现在我们来探讨下Kotlin扩展函数的实现原理。我们知道java 是静态类型语言,无法在运行时改变其类结构(函数、属性),只能通过继承的方式创造新类来扩展属性和方法,Kotlin 也是静态语言,也同样无法在运行时改变其类结构(函数、属性)。那么,Kotlin 的扩展函数有是怎么实现的呢? 。我们利用Android studio 的Kotlin Tools 将Kotlin代码,反编译为Java源码:

(kotlin-android 会将Kotlin 源代码,编译成 java class 文件,从而运行在jvm 上面)

image
image

上图是反编译后的Java 源码,是不是一切都清楚了,和我们上面的Java 工具类的实现是一样的。

4. 使用inline (内联函数)修饰符优化使用频繁的扩展函数的调用效率

我们写两个实现同样功能的扩展函数,一个不用inline 修饰,一个用inline 修饰。

fun Any.log(msg: String) {
    Log.d("_" + this::class.java.simpleName, msg)
}
inline fun  Any.log1(msg: String) {
    Log.d("_" + this::class.java.simpleName, msg)
}

调用这两个方法的代码

@Test
    fun extendTestCase() {
        val person = Person("jack", 18, "男", "爱好女")
        log(person.toJson())
        log1(person.toJson())
    }

反编译Kotlin 字节码查看对应的Java 实现

image

可以看到 :

在调用log1(person.toJson()) 时是直接将 log1 的函数体(内部实现)复制了过来。

why not?下面我们来看内联函数的说明

程序在顺序执行的过程中遇到函数调用,首先要保护现场(压栈)->跳转到函数执行处->恢复现场(出栈)(此处并没有详细展开大概知道流程即可),这样一进一出无疑损耗了性能,于是乎产生了内联函数。
内联函数并没有压栈出栈的操作,而是直接将内联函数的函数体复制粘贴到了调用的位置,这样虽然减少了性能损耗,但是编译过程中实际的代码量变大了,这就是简单的空间换时间。
简而言之:内联函数就将其函数体复制粘贴过来
内联函数的说明见:https://www.jianshu.com/p/7cb426ee324c,通俗易懂

5. 小结

  1. 使用Kotlin 后我们可以使用扩展函数代替以前的工具类方便的实现一些通用的工具方法,例如打印log、toJson,showToast 等等。
  2. 在Android 上面的Kotlin 都是编译Java class 文件,我们可以利用Android studio 的Kotlin Tools 将Kotlin代码,反编译为Java源码来了解Kotlin 的内部实现,特别是在使用Kotlin遇到问题时,可以通过这种方式来定位问题。
  3. 对于使用频繁的扩展函数,可以使用inline 修饰符,优化函数的调用效率。

你可能感兴趣的:(奇技淫巧之Kotlin 扩展函数(一))