Два дополнительных типа в конструкторе по умолчанию в Котлине?

Поскольку я использовал Котлин-рефлекс для вызова своего значения по умолчанию и объявил его, я вижу второй другой конструктор.

Я понял, что в мой конструктор добавлены два разных поля 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 поля и какова их логика?

Заранее спасибо.

6
0
1 199
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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, поэтому маркер необходим, чтобы избежать возможного конфликта сигнатур.

UPD: добавлен способ поиска реального конструктора, а не синтетического.

hotkey 24.12.2018 13:02

Другие вопросы по теме