Kotlin 当前的匿名对象函数名称在 Kotlin 1.3.x 中不再有效

Gre*_*aro 5 kotlin

在 Kotlin JVM 1.2.x 中,我可以执行以下操作:

inline fun <R> Logger.logStuff(
    crossinline f: () -> R
): R {
    val methodName = object {}.javaClass.enclosingMethod.name
    try {
        this.debug("$methodName : Begin")
        f()
        this.debug("$methodName : End")
    } catch (ex: Exception) {
        this.error("$methodName : Threw exception : $ex")
        throw ex
    }
}

class Foo {
    fun doStuff() = log.logStuff {
        1 + 3
    }
}
Run Code Online (Sandbox Code Playgroud)

这会给我这样的日志:

Foo : doStuff : Begin
Foo : doStuff : End
Run Code Online (Sandbox Code Playgroud)

但是,升级到 Kotlin 1.3.50(从 1.2.x 开始)后,我收到如下日志:

Foo : logStuff : Begin
Foo : logStuff : End
Run Code Online (Sandbox Code Playgroud)

我知道currentThread().stackTrace[1].methodName要获取封闭的方法名称,但我希望避免这种情况。

还有其他方法获取当前函数名称吗?

Mur*_*kai 2

您可以将 logStuff() fun 转换为扩展并使您的 R 类型具体化,如下所示:

inline fun <reified R : Any> R.logStuff() {
    val methodName = object {}.javaClass.enclosingMethod.name
} 
Run Code Online (Sandbox Code Playgroud)

22 年 8 月 12 日更新:

这个解决方案有什么区别以及给我们带来了什么?

我们可以稍微改进一下以更好地可视化它:

inline fun <reified R : Any> R?.log(msg: String?) = this?.run {
    val objectId: String = this::class.simpleName ?: this::class.hashCode().toString()
    val methodName = object {}.javaClass.enclosingMethod?.name
    Log.d("TAG", "$objectId.$methodName() {\n  $msg\n}")
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以像这样使用它:

class ExampleClass {
    fun exampleFun() {
        log("test")
    }
}
Run Code Online (Sandbox Code Playgroud)

它会给我们这样的输出:

ExampleClass.exampleFun() {
  test
}
Run Code Online (Sandbox Code Playgroud)

由于日志函数是内联的,因此代码将在“exampleFun()”内执行。

正如您所看到的,输出中会有一个“当前函数名称”。另请注意,在某些情况下,类可能没有 simpleName,这就是为什么需要使用 hashCode。

22 年 1 月 30 日更新:

如果以上方法不适合您,请尝试以下操作:

inline fun <reified R : Any> R?.log(msg: String?) = this?.run {
    val objectId: String = this::class.simpleName ?: this::class.hashCode().toString()
    val methodName = StackWalker.getInstance().walk { frames ->
        frames.findFirst().map { it.methodName }.orElse(null)
    }
    println("$objectId.$methodName() {\n  $msg\n}")
}
Run Code Online (Sandbox Code Playgroud)