Два дополнительных типа в конструкторе по умолчанию в Kotlin?
Так как я использовал kotlin-рефлекси для вызова моего дефолта и объявил его, я вижу второй другой конструктор.
Я понял, что два разных поля int arg3 и kotlin.jvm.internal.DefaultConstructorMarker arg4 добавлены в мой конструктор.
data class Model(
@SerializedName("xyz") val entity: String?,
@SerializedName("abc") val id: Long? = null
)
val constructors = clazz.declaredConstructors // how I call the constructors
Мой актуальный вопрос: почему у меня есть эти 2 поля и какова логика этого?
Заранее спасибо.
1 ответ
Эти два параметра добавляются в специальные синтетические члены, сгенерированные компилятором Kotlin для всех функций и конструкторов с параметрами по умолчанию.
С помощью отражения Java вы можете отфильтровать эти искусственные функции и конструкторы, проверив isSynthetic()
и найти тот, которого нет.
Целочисленный параметр - это битовая маска. Когда такая функция вызывается из Kotlin, битовая маска генерируется и передается в качестве аргумента. Биты показывают, каким из параметров функции по умолчанию передаются явные аргументы, а какие должны использовать значение по умолчанию.
DefaultConstructorMarker
Параметр используется, чтобы убедиться, что нет синтетического конструктора (принимающего битовую маску) с другим конструктором, который имеет сигнатуру с теми же аргументами и Int
в конце. Аргумент, переданный параметру маркера, никак не используется и всегда null
,
Фактически, есть два метода или конструктора, сгенерированных для каждой функции или конструктора, соответственно, которые имеют по крайней мере один параметр по умолчанию: один с той же сигнатурой, что и объявленный, и без дополнительных параметров, а другой также принимает битовую маску и маркер.
Если вы проверите байт-код такой функции, вы найдете примерно следующее для объявления функции:
fun foo(bar: String, baz: List<String> = emptyList(), qux: Set<String> = emptySet()) = 0
Реальный метод в байт-коде:
// 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
И сгенерированная оболочка, которая обрабатывает битовую маску и вычисляет значения по умолчанию, если необходимо, является отдельным методом:
// 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
Обратите внимание, как последний проверяет битовую маску (с ILOAD 3
, ICONST_x
, IAND
а затем условно (когда IFEQ Lx
не пропускает) оценивает аргументы по умолчанию.
Конструкторы отличаются от обычных функций тем, что у них не может быть суффикса $default
в названии, поэтому маркер необходим, чтобы избежать возможного конфликта подписей.