How to generate a null using Kotlin KClass property simpleName

paj*_*to0 6 kotlin

The spec for KClass.simpleName on Kotlin 1.3.41 for Common, JS, JVM and Native is:

The simple name of the class as it was declared in the source code, or null if the class has no name (if, for example, it is an anonymous object literal).

To generate a null seems straightforward: get the simpleName of the KClass generated from an anonymous object literal. The following code is a failed attempt to do just that:

interface Human { fun think(): String }

@Test fun `when finding the name of an anonymous object verify the name is null`() {
    fun start(man: Human) = println(man.think())

    start(object: Human {
        val name = this::class.simpleName
        override fun think() = "Thinking really hard! Name is: $name" // name == 2
    })
}
Run Code Online (Sandbox Code Playgroud)

My expectation was that name should be null. Why is the value of name 2? How should I change the code to get the value of name to be null?

moh*_*sen 1

我稍微改变了你的代码:

interface Human { fun think(): String }

fun main() {

    fun start(man: Human) = println(man.think())

    start(object: Human {
        val name = this::class.java.simpleName
        override fun think() = "Thinking really hard! Name is: $name" // name == 2
    })
}
Run Code Online (Sandbox Code Playgroud)

为什么名称的值为2?

现在让我们看一下字节码。
这是该函数的字节码的一部分start。正如您所看到的,它实现了Function0并且有一个invoke采用Human.

final class com/example/customview/TestKt$main$1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function1 {


  // access flags 0x1041
  public synthetic bridge invoke(Ljava/lang/Object;)Ljava/lang/Object;
    ALOAD 0
    ALOAD 1
    CHECKCAST com/example/customview/Human
    INVOKEVIRTUAL com/example/customview/TestKt$main$1.invoke (Lcom/example/customview/Human;)V
    GETSTATIC kotlin/Unit.INSTANCE : Lkotlin/Unit;
    ARETURN
    MAXSTACK = 2
    MAXLOCALS = 2

  // access flags 0x11
  public final invoke(Lcom/example/customview/Human;)V

   ....

Run Code Online (Sandbox Code Playgroud)

这是匿名对象字节码的一部分,您可以看到它实现了Human

public final class com/example/customview/TestKt$main$2 implements com/example/customview/Human {

  OUTERCLASS com/example/customview/TestKt main ()V

  // access flags 0x12
  private final Ljava/lang/String; name

   .....

Run Code Online (Sandbox Code Playgroud)

你可以从字节码中看到你的匿名类的名称实际上是TestKt$main$2. 因为编译器自动为您的文件生成一个类,即TestKt,并为 main 函数生成另一个类,即TestKt$Main。那么对于每个函数或匿名类,都会生成另一个类,并且它们按顺序命名。例如,这里的函数start有一个名称TestKt$main$1为 extendsLambda和 的类Function0。如果您在主函数之间添加一个虚拟方法,如下所示:

fun main() {

    fun start(man: Human) = println(man.think())

    fun nothing() = {}

    start(object: Human {
        val name = this::class.java.simpleName
        override fun think() = "Thinking really hard! Name is: $name" // name == 3 this time
    })
}
Run Code Online (Sandbox Code Playgroud)

那么你的班级名称就是3 这就是为什么它是 2 的答案

我应该如何更改代码以使名称的值为空?

你可以使用qualifiedName代替simpleName. 这一定是文档中的错误,只需忽略括号中的部分,即“例如,如果它是匿名对象文字”。这是文档中关于 QualifiedName 的说法:

类的完全限定的点分隔名称,如果该类是本地类或匿名对象的类,则为 null。

它就像它说的那样工作。

start(object: Human {
        val name = this::class.qualifiedName
        override fun think() = "Thinking really hard! Name is: $name" // name == null
    })
Run Code Online (Sandbox Code Playgroud)