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
})
}
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
?
Classes in Kotlin are declared using the keyword class: class Person { } The class declaration consists of the class name, the class header (specifying its type parameters, the primary constructor, and some other things), and the class body surrounded by curly braces.
Kotlin does not have a new keyword. The process of creating instances of nested, inner, and anonymous inner classes is described in Nested classes. Classes can be derived from each other and form inheritance hierarchies. Learn more about inheritance in Kotlin. A class may be declared abstract, along with some or all of its members.
However, when a property needs a backing field, Kotlin provides it automatically. This backing field can be referenced in the accessors using the field identifier: Copied! The field identifier can only be used in the accessors of the property.
Learn more about inheritance in Kotlin. A class may be declared abstract, along with some or all of its members. An abstract member does not have an implementation in its class. You don't need to annotate abstract classes or functions with open. You can override a non-abstract open member with an abstract one.
I changed your code a little bit:
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
})
}
Why is the value of name 2?
and now let's look at the bytecode for this.
this is part of the bytecode for the start
function. as you can see it implements Function0
and it has an invoke
method that takes a 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
....
and this is part of the bytecode for the anonymous object as you can see it implements 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
.....
you can see from the bytecode that the name of your anonymous class is actually TestKt$main$2
. because the compiler automatically generated a class for your file that is TestKt
and another class for the main function that is TestKt$Main
. then for every function or anonymous class, another class will be generated, and they are named by order. for example, here function start
has a class with the name TestKt$main$1
that extends Lambda
and Function0
.
if you add a dummy method in between in the main function like this:
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
})
}
then the name of your class would be 3
that is the answer for why it's 2
How should I change the code to get the value of the name to be null?
you can use qualifiedName
instead of simpleName
. this must be a mistake in the documentation, just ignore the part in the parenthesis which says "if, for example, it is an anonymous object literal".
this is what the doc says about qualifiedName:
The fully qualified dot-separated name of the class, or null if the class is local or a class of an anonymous object.
and it works as it said.
start(object: Human {
val name = this::class.qualifiedName
override fun think() = "Thinking really hard! Name is: $name" // name == null
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With