эта ссылка в ленивом инициализаторе свойства расширения kotlin

Я пробую Kotlin и хочу реализовать ленивое свойство расширения для Activity:

/**
 * Activity module
 */
val Activity.activityModule: ActivityModule by lazy {
    ActivityModule(this)
}

Ошибки компилятора с:

'this' is not defined in this context

Как я могу квалифицировать это как Activity this? Я прочитал руководство, но не могу его получить. this@Activity говорит, что ссылка не разрешена.


person Motorro    schedule 21.12.2015    source источник
comment
Можете ли вы показать больше кода? Объявление класса с этим свойством?   -  person Eugene Krivenja    schedule 21.12.2015
comment
@EugeneKrivenja, это свойство верхнего уровня. Предназначен для использования в методах подкласса Activity для создания модуля кинжала. Поэтому он определяется в отдельном файле. Что-то вроде что   -  person Motorro    schedule 21.12.2015
comment
См. также youtrack.jetbrains.com/issue/KT-13053 с одним возможным обходным решением.   -  person Vadzim    schedule 27.07.2016


Ответы (4)


Делегат lazy в Kotlin не имеет ссылки на класс-член свойства.

Я вижу два решения:

  1. преобразовать его в функцию расширения
  2. реализовать собственный делегат
person Eugene Krivenja    schedule 21.12.2015

В других ответах здесь указано, что невозможно ссылаться на this в текущей реализации приемника lazy в stdlib, и что можно реализовать свой собственный делегат. Поэтому я решил реализовать его и опубликовать здесь...:

class LazyWithReceiver<This,Return>(val initializer:This.()->Return)
{
    private val values = WeakHashMap<This,Return>()

    @Suppress("UNCHECKED_CAST")
    operator fun getValue(thisRef:Any,property:KProperty<*>):Return = synchronized(values)
    {
        thisRef as This
        return values.getOrPut(thisRef) {thisRef.initializer()}
    }
}

здесь это некоторый код, показывающий, как его использовать.

В этой реализации используется слабая хеш-карта для хранения отдельного значения для каждого получателя... это имеет несколько последствий...:

  • отдельные экземпляры, которые структурно равны, будут иметь одно и то же значение.

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

person Eric    schedule 28.06.2016

lazy вызывает функцию initializer при первом доступе к ней, а затем сохраняет значение, возвращенное initializer, чтобы возвращать это значение при последующих обращениях.

Экземпляр Lazy может хранить ровно одно значение. Когда вы делегируете свойство расширения экземпляру Lazy, вы получаете один экземпляр Lazy, обслуживающий getValue запросов от всех экземпляров типа получателя, в вашем случае это Activity. Это приводит к тому, что Lazy вычисляет значение только для первого Activity и использует это значение во всех последующих вызовах для других экземпляров Activity.

Поэтому, хотя синтаксически возможно передать Activity инициализатору в качестве получателя и указать его как this внутри, как предлагает @voddan в этом ответе, сам Lazy не может хранить разные значения для разных приемников.

Возможность иметь внешнее хранилище для свойств расширения, вероятно, может быть покрыта функцией «Прикрепленные свойства» KT- 7210. Я не думаю, что Lazy должен иметь такую ​​возможность, так как это значительно усложняет ее реализацию.

person Ilya    schedule 21.12.2015
comment
Верно. Не правильно понял. Как только верхний уровень будет разрешен статически - будет создан единственный экземпляр - person Motorro; 21.12.2015

Я думаю, что нет возможности получить доступ к Activity из тела lazy, по крайней мере, с текущей подписью\реализацией: fun <T> lazy(initializer: () -> T): Lazy<T>

Для этого подпись должна иметь вид

fun <A, T> lazy(initializer: A.() -> T): Lazy2<A, T>

Вы можете реализовать такую ​​расширенную функцию самостоятельно или\и сообщить об этом как о проблеме с stdlib

person voddan    schedule 21.12.2015
comment
Какой экземпляр типа T, по вашему мнению, должен быть передан инициализатору в качестве получателя? - person Ilya; 21.12.2015
comment
@Ilya Ilya Я думаю, что thisRef as T должен быть получателем: kotlinlang.org/docs/reference/delegated- свойства.html - person voddan; 21.12.2015
comment
Таким образом, эта ленивая реализация будет ограничена возвратом значений того же типа, что и получатель? - person Ilya; 21.12.2015
comment
Спасибо, парни! В любом случае, это не работает со свойством расширения верхнего уровня, поскольку при определении инициализатора нет значения this и thisRef. Таким образом, вы приходите к тому же результату. Закончилось функцией расширения. - person Motorro; 21.12.2015
comment
@Motorro, конечно, есть, просто нужно переписать стандартную библиотеку. Я сообщил о вашей проблеме команде kotlin - person voddan; 22.12.2015
comment
@voddan, не могли бы вы поделиться ссылкой на отчет? - person Motorro; 22.12.2015
comment
@voddan Я создал суть здесь с базовой реализацией. Было бы здорово, если бы вы прокомментировали это и указали, как бы вы создали свойство верхнего уровня с инициализатором выше. - person Motorro; 22.12.2015