Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to generate a null using Kotlin KClass property simpleName

Tags:

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
    })
}

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?

like image 319
pajato0 Avatar asked Jul 27 '19 06:07

pajato0


People also ask

How do you declare a class in Kotlin?

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.

What is nested classes in Kotlin?

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.

How to get the backing field of a property in Kotlin?

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.

What is the difference between abstract and open in Kotlin?

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.


1 Answers

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
    })
like image 137
Mohsen Avatar answered Oct 16 '22 03:10

Mohsen