This link is in the lazy initializer of the Kotlin extension property

I am trying Kotlin and want to implement the lazy extension property for Activity:

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

Compiler errors using:

 'this' is not defined in this context 

How can I qualify this as Activity this? I read the manual , but could not get it. this@Activity says the link is not resolved.

+6
source share
4 answers

The Kotlin lazy delegate does not have a reference to the property item class.

I see two solutions:

  • converts it to an extension function
  • implement your own delegate
+2
source

lazy calls the initializer function the first time it is accessed, and then saves the value returned by initializer to return this value on sequential access.

A lazy instance is capable of storing exactly one value. When you delegate the extension property to the lazy instance, you get a single lazy instance serving getValue requests from all instances of the receiver type, in your case it is Activity . This results in calculating the lazy value only for the first Activity and using this value for all subsequent calls for other Activity instances.

Therefore, although you can syntactically pass Activity to the initializer as a receiver and call it this inside, since @voddan offers this answer , lazy itself is not able to store a different value for different receivers.

The ability to have external storage for extension properties can probably be covered by the Attached Properties function of KT-7210 . I do not think lazy should have this ability, as this greatly complicates its implementation.

+3
source

Other answers here indicate that it is not possible to refer to this in the current lazy receiver stdlib implementation, and that you can implement your own delegate. Therefore, I decided to implement it and publish it here ...:

 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()} } } 

Here is some code that shows how to use it.

This implementation uses a weak hash map to store a separate value for each recipient ... this has several consequences ...:

  • different instances that are structurally equal will have the same value.

  • in some cases, values ​​that have already been initialized for some recipient can be garbage collected, which means that the initializer can be called again to reinitialize the value if it is accessed again.

+2
source

I think there is no way to access Activity from the lazy body, at least with the current signature \ implementation: fun <T> lazy(initializer: () -> T): Lazy<T>

To do this, the signature should look like

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

You can implement such an extended function yourself or \ and report it as a problem with stdlib

+1
source

All Articles