0
# Lazy Property Delegation
1
2
Property delegation pattern for lazy dependency retrieval using the DIAware interface with automatic initialization, caching, and optional triggers for controlling when dependencies are resolved.
3
4
## Capabilities
5
6
### DIAware Interface
7
8
Core interface that enables lazy property delegation for dependency injection with context and trigger support.
9
10
```kotlin { .api }
11
/**
12
* Base interface for classes that use lazy dependency injection
13
*/
14
interface DIAware {
15
/** Reference to the DI container */
16
val di: DI
17
18
/** Context for scoped dependency resolution */
19
val diContext: DIContext<*>
20
21
/** Optional trigger controlling when dependencies are resolved */
22
val diTrigger: DITrigger?
23
}
24
25
/**
26
* Property delegate interface for lazy dependency resolution
27
* @param V Type of the value provided by the delegate
28
*/
29
interface LazyDelegate<V> {
30
/** Retrieve the value (may trigger initialization) */
31
operator fun getValue(thisRef: Any?, property: KProperty<*>): V
32
33
/** Create the property delegate */
34
operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): LazyDelegate<V>
35
36
/** Check if the value has been initialized */
37
fun isInitialized(): Boolean
38
}
39
40
/**
41
* Trigger interface for controlling dependency resolution timing
42
*/
43
interface DITrigger {
44
/** Trigger the resolution process */
45
fun trigger()
46
}
47
```
48
49
### Instance Property Delegates
50
51
Lazy property delegates for retrieving single instances of dependencies with automatic caching and initialization.
52
53
```kotlin { .api }
54
/**
55
* Get a lazy delegate for an instance of type T
56
* @param tag Optional tag for disambiguation
57
* @return LazyDelegate that resolves to an instance of T
58
*/
59
fun <T : Any> DIAware.instance(tag: Any? = null): LazyDelegate<T>
60
61
/**
62
* Get a lazy delegate for an instance with explicit type token
63
* @param type TypeToken representing the type to retrieve
64
* @param tag Optional tag for disambiguation
65
* @return LazyDelegate that resolves to an instance of T
66
*/
67
fun <T : Any> DIAware.Instance(type: TypeToken<out T>, tag: Any? = null): LazyDelegate<T>
68
69
/**
70
* Get a lazy delegate for an instance with factory argument
71
* @param argType TypeToken for the argument type
72
* @param type TypeToken for the return type
73
* @param tag Optional tag for disambiguation
74
* @param arg Function providing the argument for the factory
75
* @return LazyDelegate that resolves to an instance of T
76
*/
77
fun <A, T : Any> DIAware.Instance(
78
argType: TypeToken<in A>,
79
type: TypeToken<T>,
80
tag: Any? = null,
81
arg: () -> A
82
): LazyDelegate<T>
83
84
/**
85
* Get a lazy delegate for a nullable instance
86
* @param tag Optional tag for disambiguation
87
* @return LazyDelegate that resolves to an instance of T or null if not found
88
*/
89
fun <T : Any> DIAware.instanceOrNull(tag: Any? = null): LazyDelegate<T?>
90
91
/**
92
* Get a lazy delegate for a nullable instance with explicit type token
93
* @param type TypeToken representing the type to retrieve
94
* @param tag Optional tag for disambiguation
95
* @return LazyDelegate that resolves to an instance of T or null if not found
96
*/
97
fun <T : Any> DIAware.InstanceOrNull(type: TypeToken<out T>, tag: Any? = null): LazyDelegate<T?>
98
```
99
100
### Provider Property Delegates
101
102
Lazy property delegates for retrieving provider functions that create new instances each time they are called.
103
104
```kotlin { .api }
105
/**
106
* Get a lazy delegate for a provider function
107
* @param tag Optional tag for disambiguation
108
* @return LazyDelegate that resolves to a function () -> T
109
*/
110
fun <T : Any> DIAware.provider(tag: Any? = null): LazyDelegate<() -> T>
111
112
/**
113
* Get a lazy delegate for a provider with explicit type token
114
* @param type TypeToken representing the type to provide
115
* @param tag Optional tag for disambiguation
116
* @return LazyDelegate that resolves to a function () -> T
117
*/
118
fun <T : Any> DIAware.Provider(type: TypeToken<out T>, tag: Any? = null): LazyDelegate<() -> T>
119
120
/**
121
* Get a lazy delegate for a provider curried from a factory
122
* @param argType TypeToken for the argument type
123
* @param type TypeToken for the return type
124
* @param tag Optional tag for disambiguation
125
* @param arg Function providing the argument for currying
126
* @return LazyDelegate that resolves to a provider function
127
*/
128
fun <A, T : Any> DIAware.Provider(
129
argType: TypeToken<in A>,
130
type: TypeToken<out T>,
131
tag: Any? = null,
132
arg: () -> A
133
): DIProperty<() -> T>
134
135
/**
136
* Get a lazy delegate for a nullable provider function
137
* @param tag Optional tag for disambiguation
138
* @return LazyDelegate that resolves to a function (() -> T)? or null if not found
139
*/
140
fun <T : Any> DIAware.providerOrNull(tag: Any? = null): LazyDelegate<(() -> T)?>
141
142
/**
143
* Get a lazy delegate for a nullable provider with explicit type token
144
* @param type TypeToken representing the type to provide
145
* @param tag Optional tag for disambiguation
146
* @return LazyDelegate that resolves to a function (() -> T)? or null if not found
147
*/
148
fun <T : Any> DIAware.ProviderOrNull(type: TypeToken<out T>, tag: Any? = null): LazyDelegate<(() -> T)?>
149
```
150
151
### Factory Property Delegates
152
153
Lazy property delegates for retrieving factory functions that take arguments and create instances.
154
155
```kotlin { .api }
156
/**
157
* Get a lazy delegate for a factory function
158
* @param tag Optional tag for disambiguation
159
* @return LazyDelegate that resolves to a function (A) -> T
160
*/
161
fun <A, T : Any> DIAware.factory(tag: Any? = null): LazyDelegate<(A) -> T>
162
163
/**
164
* Get a lazy delegate for a factory with explicit type tokens
165
* @param argType TypeToken for the argument type
166
* @param type TypeToken for the return type
167
* @param tag Optional tag for disambiguation
168
* @return LazyDelegate that resolves to a function (A) -> T
169
*/
170
fun <A, T : Any> DIAware.Factory(
171
argType: TypeToken<in A>,
172
type: TypeToken<out T>,
173
tag: Any? = null
174
): LazyDelegate<(A) -> T>
175
176
/**
177
* Get a lazy delegate for a nullable factory function
178
* @param tag Optional tag for disambiguation
179
* @return LazyDelegate that resolves to a function ((A) -> T)? or null if not found
180
*/
181
fun <A, T : Any> DIAware.factoryOrNull(tag: Any? = null): LazyDelegate<((A) -> T)?>
182
183
/**
184
* Get a lazy delegate for a nullable factory with explicit type tokens
185
* @param argType TypeToken for the argument type
186
* @param type TypeToken for the return type
187
* @param tag Optional tag for disambiguation
188
* @return LazyDelegate that resolves to a function ((A) -> T)? or null if not found
189
*/
190
fun <A, T : Any> DIAware.FactoryOrNull(
191
argType: TypeToken<in A>,
192
type: TypeToken<out T>,
193
tag: Any? = null
194
): LazyDelegate<((A) -> T)?>
195
```
196
197
### Constant Property Delegates
198
199
Lazy property delegates for retrieving constant values using property names as tags automatically.
200
201
```kotlin { .api }
202
/**
203
* Get a lazy delegate for a constant using the property name as tag
204
* @return LazyDelegate that resolves to the constant value
205
*/
206
fun <T : Any> DIAware.constant(): LazyDelegate<T>
207
208
/**
209
* Get a lazy delegate for a nullable constant using the property name as tag
210
* @return LazyDelegate that resolves to the constant value or null if not found
211
*/
212
fun <T : Any> DIAware.constantOrNull(): LazyDelegate<T?>
213
```
214
215
### Named Property Access
216
217
Wrapper class that automatically uses property names as tags for all dependency lookups.
218
219
```kotlin { .api }
220
/**
221
* Wrapper class that uses property names as tags automatically
222
*/
223
@JvmInline
224
value class Named(private val base: DIAware) : DIAware by base
225
226
/**
227
* Get a named wrapper that uses property names as tags
228
*/
229
val DIAware.named: Named
230
231
// Named delegates (same signatures as DIAware but use property name as tag)
232
fun <T : Any> Named.instance(): LazyDelegate<T>
233
fun <T : Any> Named.provider(): LazyDelegate<() -> T>
234
fun <A, T : Any> Named.factory(): LazyDelegate<(A) -> T>
235
// ... all other delegate methods without tag parameters
236
```
237
238
### Context and Trigger Management
239
240
Advanced property delegation with custom contexts and triggers for fine-grained control over dependency resolution.
241
242
```kotlin { .api }
243
/**
244
* Create a new DIAware instance with different context and/or trigger
245
* @param context New context for scoped dependencies
246
* @param trigger New trigger for controlling resolution timing
247
* @return New DIAware instance with updated context/trigger
248
*/
249
fun DIAware.On(context: DIContext<*> = this.diContext, trigger: DITrigger? = this.diTrigger): DI
250
251
/**
252
* Create a new DIAware instance with typed context
253
* @param context Context value
254
* @param trigger Optional trigger for controlling resolution timing
255
* @return New DIAware instance with typed context
256
*/
257
fun <C : Any> DIAware.on(context: C, trigger: DITrigger? = this.diTrigger): DI
258
259
/**
260
* Create a new instance using dependency injection within a closure
261
* @param creator Function that creates the instance using DirectDI
262
* @return LazyDelegate that creates the instance when accessed
263
*/
264
fun <T> DIAware.newInstance(creator: DirectDI.() -> T): LazyDelegate<T>
265
```
266
267
### Property Delegate Implementation
268
269
Internal implementation classes for property delegation (typically not used directly).
270
271
```kotlin { .api }
272
/**
273
* Property delegate implementation (internal)
274
* @param V Type of the delegated value
275
*/
276
class DIProperty<V>(
277
trigger: DITrigger?,
278
context: DIContext<*>,
279
originalContext: DIContext<*>,
280
f: (DIContext<*>, KProperty<*>) -> V
281
) : LazyDelegate<V>
282
283
/**
284
* Mapped property delegate that transforms values
285
* @param I Input type from the original delegate
286
* @param O Output type after transformation
287
*/
288
class DIPropertyMap<I, O>(
289
private val original: LazyDelegate<I>,
290
private val map: (I) -> O
291
) : LazyDelegate<O>
292
```
293
294
**Usage Examples:**
295
296
```kotlin
297
// Basic DIAware implementation
298
class UserController : DIAware {
299
override val di: DI = appDI
300
301
// Instance delegates - singleton access
302
private val userService: UserService by instance()
303
private val database: Database by instance()
304
305
// Provider delegates - new instance each access
306
private val emailService: () -> EmailService by provider()
307
private val loggerProvider: () -> Logger by provider("fileLogger")
308
309
// Factory delegates - parameterized creation
310
private val userRepositoryFactory: (String) -> UserRepository by factory()
311
private val cacheFactory: (String) -> Cache by factory()
312
313
// Constant delegates using property name as tag
314
private val apiUrl: String by constant()
315
private val timeout: Long by constant()
316
317
// Nullable delegates for optional dependencies
318
private val analyticsService: AnalyticsService? by instanceOrNull()
319
private val debugLogger: (() -> Logger)? by providerOrNull("debug")
320
321
fun handleUser(userId: String) {
322
val service = userService // Singleton instance
323
val email = emailService() // New instance each call
324
val repo = userRepositoryFactory(userId) // Factory with argument
325
val cache = cacheFactory("user:$userId") // Another factory call
326
327
// Use constant values
328
val url = apiUrl // Resolves to constant tagged "apiUrl"
329
val timeoutMs = timeout // Resolves to constant tagged "timeout"
330
331
// Optional services
332
analyticsService?.track("user_accessed", userId)
333
debugLogger?.invoke()?.debug("Processing user $userId")
334
}
335
}
336
337
// Named property access (uses property names as tags)
338
class ConfigService : DIAware {
339
override val di: DI = configDI
340
341
// These automatically use property names as tags
342
private val databaseUrl: String by named.instance()
343
private val apiKey: String by named.instance()
344
private val retryCount: Int by named.instance()
345
private val enableLogging: Boolean by named.instance()
346
}
347
348
// Custom context and triggers
349
class ScopedController : DIAware {
350
override val di: DI = appDI
351
override val diContext: DIContext<*> = diContext<UserSession> { getCurrentSession() }
352
override val diTrigger: DITrigger = object : DITrigger {
353
override fun trigger() {
354
// Custom trigger logic - maybe validate session
355
validateCurrentSession()
356
}
357
}
358
359
// These will use the custom context and trigger
360
private val sessionData: SessionData by instance()
361
private val userPreferences: UserPreferences by instance()
362
363
// Create instances with dependency injection
364
private val processor: RequestProcessor by newInstance {
365
RequestProcessor(instance(), instance(), constant("maxRetries"))
366
}
367
368
// Change context for specific dependencies
369
private val globalCache: Cache by On(AnyDIContext).instance("global")
370
}
371
372
// Late initialization with property delegates
373
class Application : DIAware {
374
private lateinit var _di: DI
375
override val di: DI get() = _di
376
377
// These will be initialized when _di is set
378
private val configService: ConfigService by instance()
379
private val startupTasks: List<StartupTask> by instance()
380
381
fun initialize() {
382
_di = DI {
383
bind<ConfigService>() with singleton { ConfigServiceImpl() }
384
bind<List<StartupTask>>() with provider {
385
listOf(DatabaseMigration(), CacheWarming())
386
}
387
}
388
389
// Now property delegates can resolve
390
configService.load()
391
startupTasks.forEach { it.execute() }
392
}
393
}
394
```