Встроенное свойство расширения Kotlin

Я знаю, что ключевое слово inline позволяет избежать накладных расходов на вызов функции. Но я не могу понять, для чего работает встроенное свойство расширения?

Допустим, у нас есть два свойства расширения с именем foo и еще одно свойство с встроенным именем bar

val Any.foo : Long
    get() = Date().time

inline val Any.bar : Long
    get() = Date().time

Выполняя любой из них, мы получаем ожидаемый результат, текущее время.

Байт-код для этого файла следующий:

public final class InlinedExtensionPropertyKt {

  public final static getFoo(Ljava/lang/Object;)J
    @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
   L0
    ALOAD 0
    LDC "$receiver"
    INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
   L1
    LINENUMBER 9 L1
    NEW java/util/Date
    DUP
    INVOKESPECIAL java/util/Date.<init> ()V
    INVOKEVIRTUAL java/util/Date.getTime ()J
    LRETURN
   L2
    LOCALVARIABLE $receiver Ljava/lang/Object; L0 L2 0
    MAXSTACK = 2
    MAXLOCALS = 1

  public final static getBar(Ljava/lang/Object;)J
    @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
   L0
    ALOAD 0
    LDC "$receiver"
    INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
   L1
    LINENUMBER 12 L1
    NEW java/util/Date
    DUP
    INVOKESPECIAL java/util/Date.<init> ()V
    INVOKEVIRTUAL java/util/Date.getTime ()J
    LRETURN
   L2
    LOCALVARIABLE $receiver Ljava/lang/Object; L0 L2 0
    LOCALVARIABLE $i$f$getBar I L0 L2 1
    MAXSTACK = 2
    MAXLOCALS = 2

  @Lkotlin/Metadata;(mv={1, 1, 7}, bv={1, 0, 2}, k=2, d1={"\u0000\u000e\n\u0000\n\u0002\u0010\u0009\n\u0002\u0010\u0000\n\u0002\u0008\u0005\"\u0016\u0010\u0000\u001a\u00020\u0001*\u00020\u00028\u00c6\u0002\u00a2\u0006\u0006\u001a\u0004\u0008\u0003\u0010\u0004\"\u0015\u0010\u0005\u001a\u00020\u0001*\u00020\u00028F\u00a2\u0006\u0006\u001a\u0004\u0008\u0006\u0010\u0004\u00a8\u0006\u0007"}, d2={"bar", "", "", "getBar", "(Ljava/lang/Object;)J", "foo", "getFoo", "test sources for module app"})
  // compiled from: InlinedExtensionPropertyKt.kt
}

Мы видим, что оба они похожи, но отличаются только в этих строках:

foo извлечение:

    LOCALVARIABLE $receiver Ljava/lang/Object; L0 L2 0
    MAXSTACK = 2
    MAXLOCALS = 1

бар извлечение:

    LOCALVARIABLE $receiver Ljava/lang/Object; L0 L2 0
    LOCALVARIABLE $i$f$getBar I L0 L2 1
    MAXSTACK = 2
    MAXLOCALS = 2

Я действительно не понимаю, что здесь происходит. Может ли кто-нибудь указать мне, что такое поведение, или эквивалент в java, или какое-то использование для этого?

Изменить

Учитывая, что компилятор заменит содержимое встроенного свойства, может быть удобно встроить каждое свойство расширения, не выполняющее тяжелых операций?

Спасибо


person crgarridos    schedule 21.09.2017    source источник


Ответы (1)


Из документа Котлина,

Обратите внимание: поскольку расширения фактически не вставляют элементы в классы, нет эффективного способа, чтобы свойство расширения могло иметь резервное поле.

и также,

Модификатор inline можно использовать для средств доступа к свойствам, у которых нет резервного поля.

Как упоминалось выше, свойство встроенного расширения не имеет резервного поля. Вы можете рассматривать свойство расширения как пару статических получателей / установщиков, например:

//In Kotlin
var Any.foo : Long
    get() = Date().time
    set(value) {
        //Cannot access field here since extension property cannot have backing field
        //Do something with `obj`
    }

//In Java
public static long getFoo(Object obj) {
    return new Date().getTime();
}
public static void setFoo(Object obj) {
    //Do something with `obj`
}

Таким образом, встроенное свойство означает, что код функции получения / установки будет встроен в сайт вызова при доступе к свойству (так же, как и обычные встроенные функции).

//In Kotlin
val x = "".foo
val y = "".bar

//Generated code
val x = InlinedExtensionPropertyKt.getFoo("")
val y = Date().time

Что касается байт-кода, который вы публикуете в вопросе, извините, что я не могу объяснить, что происходит. Но вы можете попробовать взглянуть на байт-код следующего кода:

fun get() {
    val x = "".foo
    val y = "".bar
}

, где "".foo вызовет функцию получения, а "".bar - нет.

person BakaWaii    schedule 21.09.2017
comment
Спасибо за ответ, я думаю, что байт-код - это больше, чем вы предлагаете. Вы считаете, что хорошо встроить каждое свойство расширения? - person crgarridos; 22.09.2017
comment
@crgarridos Для меня в большинстве случаев накладные расходы на вызов функций не имеют большого значения. Я бы подумал об использовании inline, когда мне нужны параметры повторного типа. Вы можете ознакомиться с этим вопросом для справки. . - person BakaWaii; 22.09.2017
comment
Нашел применение: inline val <reified T> T.TAG : String get() = T::class.java.simpleName :) - person crgarridos; 28.09.2017
comment
Да, это правильное и хорошее использование параметра овеществленного типа. Чтобы было лучше, я бы использовал T::class.qualifiedName, как предлагалось здесь из-за этого проблема. - person BakaWaii; 28.09.2017