如何使用Kotlin KClass属性simpleName生成null
Kotlin 1.3.41中针对Common、JS、JVM和Native的如何使用Kotlin KClass属性simpleName生成null,kotlin,Kotlin,Kotlin 1.3.41中针对Common、JS、JVM和Native的KClass.simpleName规范是: 源代码中声明的类的简单名称,或 如果类没有名称(例如,如果它是匿名 对象文字) 生成null似乎很简单:获取从匿名对象文本生成的KClass的simpleName。以下代码是执行此操作的失败尝试: interface Human { fun think(): String } @Test fun `when finding the name of an anonymous ob
KClass.simpleName
规范是:
源代码中声明的类的简单名称,或
如果类没有名称(例如,如果它是匿名
对象文字)
生成null似乎很简单:获取从匿名对象文本生成的KClass的simpleName。以下代码是执行此操作的失败尝试:
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
})
}
我的期望是名称应该是
null
。为什么名称2的值是?我应该如何更改代码以将name的值设置为null
?从Kotlin中的simpleName获取null的唯一方法是引用Java构建的类,或者用类定义覆盖simpleName。默认情况下,JVM上Kotlin生成的类始终具有已定义的simpleName属性。我稍微更改了您的代码:
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
})
}
为什么名称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
....
这是匿名对象字节码的一部分,您可以看到它实现了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
.....
您可以从字节码中看到匿名类的名称实际上是TestKt$main$2
。因为编译器自动为您的文件生成了一个类TestKt
,为主函数生成了另一个类TestKt$main
。然后,对于每个函数或匿名类,将生成另一个类,并按顺序命名它们。例如,函数start
有一个名为TestKt$main$1
的类,它扩展了Lambda
和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
})
}
那么您的类的名称将是3
这就是为什么它是2的答案
我应该如何更改代码以使名称的值为null
您可以使用qualifiedName
而不是simpleName
。这一定是文档中的一个错误,忽略括号中的“如果,例如,它是匿名对象文字”部分。
以下是医生对qualifiedName的看法:
类的完全限定点分隔名称,如果类是本地的或匿名对象的类,则为null
正如它所说的那样
start(object: Human {
val name = this::class.qualifiedName
override fun think() = "Thinking really hard! Name is: $name" // name == null
})
也许这是您的测试框架的一些奇怪行为。当我将粘贴的代码复制到
fun main()
中时,它会打印null
——当我将它粘贴到类函数中并从main方法运行它时,我能够重现该问题。Kotlin 1.3.41无法使用Kotlin 1.3.41进行复制。你能提供一份详细的报告吗?您使用的是什么版本的Kotlin?您的构建环境是什么?你在使用什么测试框架?我至少有一个JS报告null和JVM报告2的报告。使用OpenJDK 12.0.1、Windows 10和Gradle 4.10/Gradle 5.5.1克隆存储库后,HmmmmStill无法复制。执行clean build
成功完成。测试报告显示9个成功测试和0个失败。