Since I've been using kotlin-reflect to invoke my default and declared one, I see second different constructor.
I have realized that two different fields int arg3 and kotlin.jvm.internal.DefaultConstructorMarker arg4 added to my constructor.
data class Model(
@SerializedName("xyz") val entity: String?,
@SerializedName("abc") val id: Long? = null
)
val constructors = clazz.declaredConstructors // how I call the constructors
My actual question is why I have these 2 fields and what is the logic behind it ?
Thanks in advance.
In Kotlin, there are two constructors: Primary constructor - concise way to initialize a class. Secondary constructor - allows you to put additional initialization logic.
A constructor is a special member function that is invoked when an object of the class is created primarily to initialize variables or properties. A class needs to have a constructor and if we do not declare a constructor, then the compiler generates a default constructor.
These two parameters are added to special synthetic members generated by the Kotlin compiler for all functions and constructors with default parameters.
With Java reflection, you can filter out these synthetic functions and constructors by checking isSynthetic()
and finding the one that is not.
The integer parameter is a bit mask. When such a function is called from Kotlin, a bit mask is generated and passed as the argument. The bits show which of the default parameters of the function are passed explicit arguments and which should use the default value.
The DefaultConstructorMarker
parameter is used to ensure there's no collision of the synthetic constructor (accepting the bit mask) with a different constructor which has a signature with those same arguments and an Int
in the end. The argument passed to the marker parameter is not used in any way, and it is always null
.
In fact, there are two methods or constructors generated for each function or constructor, respectively, that has at least one default parameter: one with the same signature as declared and no additional parameters, and the other also accepting a bit mask and the marker.
If you inspect the bytecode of such a function, you will find roughly the following, for a function declaration:
fun foo(bar: String, baz: List<String> = emptyList(), qux: Set<String> = emptySet()) = 0
The real method in the bytecode is:
// access flags 0x19
// signature (Ljava/lang/String;Ljava/util/List<Ljava/lang/String;>;Ljava/util/Set<Ljava/lang/String;>;)I
// declaration: int foo(java.lang.String, java.util.List<java.lang.String>, java.util.Set<java.lang.String>)
public final static foo(
Ljava/lang/String;
Ljava/util/List;
Ljava/util/Set;
)I
// annotable parameter count: 3 (visible)
// annotable parameter count: 3 (invisible)
@Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
@Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 1
@Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 2
L0
ALOAD 0
LDC "bar"
INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
ALOAD 1
LDC "baz"
INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
ALOAD 2
LDC "qux"
INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
L1
LINENUMBER 16 L1
ICONST_0
IRETURN
L2
LOCALVARIABLE bar Ljava/lang/String; L0 L2 0
LOCALVARIABLE baz Ljava/util/List; L0 L2 1
LOCALVARIABLE qux Ljava/util/Set; L0 L2 2
MAXSTACK = 2
MAXLOCALS = 3
And the generated wrapper that handles the bit mask and calculates the default values if necessary is a separate method:
// access flags 0x1009
public static synthetic foo$default(
Ljava/lang/String;
Ljava/util/List;
Ljava/util/Set;
I
Ljava/lang/Object;
)I
ILOAD 3
ICONST_2
IAND
IFEQ L0
L1
LINENUMBER 16 L1
INVOKESTATIC kotlin/collections/CollectionsKt.emptyList ()Ljava/util/List;
ASTORE 1
L0
ILOAD 3
ICONST_4
IAND
IFEQ L2
INVOKESTATIC kotlin/collections/SetsKt.emptySet ()Ljava/util/Set;
ASTORE 2
L2
ALOAD 0
ALOAD 1
ALOAD 2
INVOKESTATIC FooKt.foo (Ljava/lang/String;Ljava/util/List;Ljava/util/Set;)I
IRETURN
MAXSTACK = 3
MAXLOCALS = 5
Note how the latter checks the bit mask (with ILOAD 3
, ICONST_x
, IAND
) and then conditionally (when IFEQ Lx
doesn't skip it) evaluates the default arguments.
Constructors are different from ordinary functions in that they can't have a suffix $default
in the name, so the marker is necessary to avoid possible signatures conflict.
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