0
# Qualifiers & Parameters
1
2
This document covers how to distinguish between similar dependencies using qualifiers and how to pass runtime parameters during dependency resolution.
3
4
## Overview
5
6
Koin provides two key mechanisms for handling complex dependency scenarios:
7
- **Qualifiers** - Distinguish between different instances of the same type
8
- **Parameters** - Pass runtime values during dependency resolution
9
10
These features enable sophisticated dependency injection patterns while maintaining type safety and clarity.
11
12
## Qualifier System
13
14
Qualifiers help distinguish between multiple definitions of the same type, enabling you to have different implementations or configurations for different contexts.
15
16
### Qualifier Interface
17
18
```kotlin { .api }
19
interface Qualifier {
20
val value: QualifierValue
21
}
22
23
typealias QualifierValue = String
24
```
25
26
### Qualifier Creation Functions
27
28
```kotlin { .api }
29
// String-based qualifiers
30
fun named(name: String): StringQualifier
31
fun qualifier(name: String): StringQualifier
32
fun _q(name: String): StringQualifier
33
34
// Enum-based qualifiers
35
fun <E : Enum<E>> named(enum: Enum<E>): Qualifier
36
fun <E : Enum<E>> qualifier(enum: Enum<E>): Qualifier
37
val <E : Enum<E>> Enum<E>.qualifier: Qualifier
38
39
// Type-based qualifiers
40
inline fun <reified T> named(): TypeQualifier
41
inline fun <reified T> qualifier(): TypeQualifier
42
inline fun <reified T> _q(): TypeQualifier
43
```
44
45
### Qualifier Implementations
46
47
```kotlin { .api }
48
data class StringQualifier(override val value: QualifierValue) : Qualifier
49
50
class TypeQualifier(val type: KClass<*>) : Qualifier {
51
override val value: QualifierValue
52
}
53
```
54
55
## Using String Qualifiers
56
57
### Basic String Qualifiers
58
59
```kotlin
60
import org.koin.core.qualifier.named
61
import org.koin.dsl.*
62
63
val networkModule = module {
64
// Different HTTP clients for different purposes
65
single<HttpClient>(named("public")) {
66
HttpClient().apply {
67
timeout = 30000
68
}
69
}
70
71
single<HttpClient>(named("authenticated")) {
72
HttpClient().apply {
73
timeout = 60000
74
addAuthInterceptor()
75
}
76
}
77
78
single<HttpClient>(named("admin")) {
79
HttpClient().apply {
80
timeout = 120000
81
addAuthInterceptor()
82
addAdminHeaders()
83
}
84
}
85
}
86
87
// Resolution with qualifiers
88
val koin = koinApplication { modules(networkModule) }.koin
89
90
val publicClient: HttpClient = koin.get(named("public"))
91
val authClient: HttpClient = koin.get(named("authenticated"))
92
val adminClient: HttpClient = koin.get(named("admin"))
93
```
94
95
### Environment-Based Qualifiers
96
97
```kotlin
98
val configModule = module {
99
single<DatabaseConfig>(named("development")) {
100
DatabaseConfig("jdbc:h2:mem:dev", "dev_user", "dev_pass")
101
}
102
103
single<DatabaseConfig>(named("production")) {
104
DatabaseConfig("jdbc:postgresql://prod-server/db", "prod_user", "prod_pass")
105
}
106
107
single<DatabaseConfig>(named("test")) {
108
DatabaseConfig("jdbc:h2:mem:test", "test_user", "test_pass")
109
}
110
}
111
112
class DatabaseService : KoinComponent {
113
private val environment = System.getenv("ENVIRONMENT") ?: "development"
114
private val config: DatabaseConfig = get(named(environment))
115
}
116
```
117
118
### Service Variant Qualifiers
119
120
```kotlin
121
val serviceModule = module {
122
// Different implementations of the same service
123
single<PaymentService>(named("stripe")) { StripePaymentService() }
124
single<PaymentService>(named("paypal")) { PayPalPaymentService() }
125
single<PaymentService>(named("mock")) { MockPaymentService() }
126
127
// Email service variants
128
single<EmailService>(named("smtp")) { SMTPEmailService() }
129
single<EmailService>(named("sendgrid")) { SendGridEmailService() }
130
single<EmailService>(named("console")) { ConsoleEmailService() }
131
}
132
```
133
134
## Using Enum Qualifiers
135
136
### Enum-Based Qualifiers
137
138
```kotlin
139
enum class ServiceTier {
140
FREE, PREMIUM, ENTERPRISE
141
}
142
143
enum class Region {
144
US_EAST, US_WEST, EUROPE, ASIA
145
}
146
147
val tierModule = module {
148
// Using enum qualifiers
149
single<FeatureSet>(ServiceTier.FREE.qualifier) {
150
FreeFeatureSet()
151
}
152
153
single<FeatureSet>(ServiceTier.PREMIUM.qualifier) {
154
PremiumFeatureSet()
155
}
156
157
single<FeatureSet>(ServiceTier.ENTERPRISE.qualifier) {
158
EnterpriseFeatureSet()
159
}
160
161
// Regional services
162
single<RegionalService>(Region.US_EAST.qualifier) {
163
USEastService()
164
}
165
166
single<RegionalService>(Region.EUROPE.qualifier) {
167
EuropeService()
168
}
169
}
170
171
// Usage with enum qualifiers
172
class TieredService : KoinComponent {
173
fun getFeatureSet(tier: ServiceTier): FeatureSet {
174
return get(tier.qualifier)
175
}
176
177
fun getRegionalService(region: Region): RegionalService {
178
return get(named(region)) // Alternative syntax
179
}
180
}
181
```
182
183
## Using Type Qualifiers
184
185
### Type-Based Qualifiers
186
187
```kotlin
188
// Marker types for different contexts
189
class ProductionContext
190
class DevelopmentContext
191
class TestContext
192
193
val contextModule = module {
194
// Type-based qualifiers
195
single<Logger>(named<ProductionContext>()) {
196
FileLogger("/var/log/app.log")
197
}
198
199
single<Logger>(named<DevelopmentContext>()) {
200
ConsoleLogger()
201
}
202
203
single<Logger>(named<TestContext>()) {
204
MockLogger()
205
}
206
207
// Cache implementations by type
208
single<Cache>(qualifier<UserCache>()) {
209
RedisCache("user-cache")
210
}
211
212
single<Cache>(qualifier<SessionCache>()) {
213
InMemoryCache()
214
}
215
}
216
217
// Usage
218
class ContextAwareService : KoinComponent {
219
private val prodLogger: Logger = get(named<ProductionContext>())
220
private val userCache: Cache = get(qualifier<UserCache>())
221
}
222
```
223
224
### Generic Type Qualifiers
225
226
```kotlin
227
// Different cache instances for different data types
228
val cacheModule = module {
229
single<Cache<User>>(named("user")) {
230
InMemoryCache<User>()
231
}
232
233
single<Cache<Product>>(named("product")) {
234
RedisCache<Product>("products")
235
}
236
237
single<Cache<String>>(named("session")) {
238
InMemoryCache<String>()
239
}
240
}
241
```
242
243
## Parameter System
244
245
Parameters allow you to pass runtime values during dependency resolution, enabling dynamic configuration and context-specific instantiation.
246
247
### ParametersHolder Class
248
249
```kotlin { .api }
250
class ParametersHolder(
251
internal val _values: MutableList<Any?> = mutableListOf(),
252
val useIndexedValues: Boolean? = null
253
) {
254
val values: List<Any?>
255
var index: Int
256
257
// Access by index
258
operator fun <T> get(i: Int): T
259
fun <T> set(i: Int, t: T)
260
261
// Access by type
262
inline fun <reified T : Any> get(): T
263
inline fun <reified T : Any> getOrNull(): T?
264
fun <T> getOrNull(clazz: KClass<*>): T?
265
fun <T> elementAt(i: Int, clazz: KClass<*>): T
266
267
// Destructuring components
268
inline operator fun <reified T> component1(): T
269
inline operator fun <reified T> component2(): T
270
inline operator fun <reified T> component3(): T
271
inline operator fun <reified T> component4(): T
272
inline operator fun <reified T> component5(): T
273
274
// Utilities
275
fun size(): Int
276
fun isEmpty(): Boolean
277
fun isNotEmpty(): Boolean
278
fun add(value: Any): ParametersHolder
279
fun insert(index: Int, value: Any): ParametersHolder
280
}
281
282
typealias ParametersDefinition = () -> ParametersHolder
283
```
284
285
### Parameter Creation Functions
286
287
```kotlin { .api }
288
// Standard parameters - indexed or type-based resolution
289
fun parametersOf(vararg parameters: Any?): ParametersHolder
290
291
// Array-based parameters - consumed one by one (indexed)
292
fun parameterArrayOf(vararg parameters: Any?): ParametersHolder
293
294
// Set-based parameters - different types of values
295
fun parameterSetOf(vararg parameters: Any?): ParametersHolder
296
297
// Empty parameters
298
fun emptyParametersHolder(): ParametersHolder
299
```
300
301
## Working with Parameters
302
303
### Basic Parameter Usage
304
305
```kotlin
306
val parameterModule = module {
307
factory<DatabaseConnection> { params ->
308
val host: String = params.get()
309
val port: Int = params.get()
310
val database: String = params.get()
311
DatabaseConnection(host, port, database)
312
}
313
}
314
315
val koin = koinApplication { modules(parameterModule) }.koin
316
317
// Pass parameters during resolution
318
val connection: DatabaseConnection = koin.get {
319
parametersOf("localhost", 5432, "myapp")
320
}
321
```
322
323
### Indexed Parameter Access
324
325
```kotlin
326
val indexedModule = module {
327
factory<ApiClient> { params ->
328
val baseUrl: String = params[0] // Get by index
329
val timeout: Int = params[1]
330
val apiKey: String = params[2]
331
ApiClient(baseUrl, timeout, apiKey)
332
}
333
}
334
335
// Usage
336
val apiClient: ApiClient = koin.get {
337
parametersOf("https://api.example.com", 30000, "secret-key")
338
}
339
```
340
341
### Type-Based Parameter Access
342
343
```kotlin
344
val typedModule = module {
345
factory<EmailService> { params ->
346
val config: EmailConfig = params.get() // Get by type
347
val logger: Logger = params.get()
348
val retries: Int = params.get()
349
EmailService(config, logger, retries)
350
}
351
}
352
353
// Usage with different types
354
val emailService: EmailService = koin.get {
355
parametersOf(
356
EmailConfig("smtp.example.com"),
357
FileLogger("/tmp/email.log"),
358
3
359
)
360
}
361
```
362
363
### Destructuring Parameters
364
365
```kotlin
366
val destructuringModule = module {
367
factory<ServiceConfig> { params ->
368
val (host: String, port: Int, ssl: Boolean) = params
369
ServiceConfig(host, port, ssl)
370
}
371
}
372
373
// Usage
374
val config: ServiceConfig = koin.get {
375
parametersOf("api.example.com", 443, true)
376
}
377
```
378
379
### Parameter Access Modes
380
381
```kotlin
382
val parameterModule = module {
383
factory<MultiTypeService> { params ->
384
// Mixed parameter resolution - by index or by type
385
val value1: String = params.get() // By type
386
val value2: Int = params.get() // By type
387
MultiTypeService(value1, value2.toString())
388
}
389
}
390
391
// Regular parameters
392
val service: MultiTypeService = koin.get {
393
parametersOf("value", 42)
394
}
395
```
396
397
## Combining Qualifiers and Parameters
398
399
### Qualified Parameterized Definitions
400
401
```kotlin
402
val combinedModule = module {
403
// Different database connections with parameters
404
factory<DatabaseConnection>(named("primary")) { params ->
405
val credentials: Credentials = params.get()
406
PrimaryDatabaseConnection(credentials)
407
}
408
409
factory<DatabaseConnection>(named("backup")) { params ->
410
val credentials: Credentials = params.get()
411
val timeout: Int = params.get()
412
BackupDatabaseConnection(credentials, timeout)
413
}
414
415
factory<DatabaseConnection>(named("readonly")) { params ->
416
val (host: String, database: String) = params
417
ReadOnlyDatabaseConnection(host, database)
418
}
419
}
420
421
// Usage with both qualifiers and parameters
422
val primary: DatabaseConnection = koin.get(named("primary")) {
423
parametersOf(Credentials("user", "pass"))
424
}
425
426
val backup: DatabaseConnection = koin.get(named("backup")) {
427
parametersOf(Credentials("backup_user", "backup_pass"), 60000)
428
}
429
430
val readonly: DatabaseConnection = koin.get(named("readonly")) {
431
parametersOf("replica.db.com", "myapp_readonly")
432
}
433
```
434
435
### Dynamic Qualifier Selection
436
437
```kotlin
438
val dynamicModule = module {
439
single<CacheService>(named("redis")) { params ->
440
val config: RedisConfig = params.get()
441
RedisCacheService(config)
442
}
443
444
single<CacheService>(named("memory")) { params ->
445
val maxSize: Int = params.getOrNull() ?: 1000
446
InMemoryCacheService(maxSize)
447
}
448
}
449
450
class CacheManager : KoinComponent {
451
fun getCacheService(type: String, config: Any? = null): CacheService {
452
return when (type) {
453
"redis" -> get(named("redis")) {
454
parametersOf(config as RedisConfig)
455
}
456
"memory" -> get(named("memory")) {
457
config?.let { parametersOf(it) } ?: emptyParametersHolder()
458
}
459
else -> throw IllegalArgumentException("Unknown cache type: $type")
460
}
461
}
462
}
463
```
464
465
## Advanced Patterns
466
467
### Factory Functions with Qualifiers
468
469
```kotlin
470
class ServiceFactory : KoinComponent {
471
fun createUserService(userId: String, tier: ServiceTier): UserService {
472
return get(tier.qualifier) { parametersOf(userId) }
473
}
474
475
fun createRegionalService(region: Region, config: RegionalConfig): RegionalService {
476
return get(region.qualifier) { parametersOf(config) }
477
}
478
}
479
480
val factoryModule = module {
481
factory<UserService>(ServiceTier.FREE.qualifier) { params ->
482
val userId: String = params.get()
483
FreeUserService(userId)
484
}
485
486
factory<UserService>(ServiceTier.PREMIUM.qualifier) { params ->
487
val userId: String = params.get()
488
PremiumUserService(userId, get<AnalyticsService>())
489
}
490
}
491
```
492
493
### Conditional Parameter Handling
494
495
```kotlin
496
val conditionalModule = module {
497
factory<ConfigurableService> { params ->
498
val config: ServiceConfig = params.get()
499
val logger: Logger? = params.getOrNull()
500
val metrics: MetricsService? = params.getOrNull()
501
502
ConfigurableService(config).apply {
503
logger?.let { setLogger(it) }
504
metrics?.let { setMetrics(it) }
505
}
506
}
507
}
508
509
// Usage with optional parameters
510
val basicService: ConfigurableService = koin.get {
511
parametersOf(ServiceConfig("basic"))
512
}
513
514
val advancedService: ConfigurableService = koin.get {
515
parametersOf(
516
ServiceConfig("advanced"),
517
FileLogger("service.log"),
518
PrometheusMetrics()
519
)
520
}
521
```
522
523
### Parameter Validation
524
525
```kotlin
526
val validationModule = module {
527
factory<ValidatedService> { params ->
528
val config: ServiceConfig = params.get()
529
val timeout: Int = params.get()
530
531
require(timeout > 0) { "Timeout must be positive" }
532
require(config.isValid()) { "Invalid service configuration" }
533
534
ValidatedService(config, timeout)
535
}
536
}
537
```
538
539
## Best Practices
540
541
### 1. Use Meaningful Qualifier Names
542
543
```kotlin
544
// Good - descriptive names
545
single<HttpClient>(named("user-api-client")) { /* ... */ }
546
single<Database>(named("primary-user-db")) { /* ... */ }
547
548
// Avoid - generic names
549
single<HttpClient>(named("client1")) { /* ... */ }
550
single<Database>(named("db")) { /* ... */ }
551
```
552
553
### 2. Consistent Qualifier Patterns
554
555
```kotlin
556
// Consistent naming pattern
557
val apiModule = module {
558
single<ApiService>(named("user-api")) { /* ... */ }
559
single<ApiService>(named("product-api")) { /* ... */ }
560
single<ApiService>(named("order-api")) { /* ... */ }
561
}
562
```
563
564
### 3. Parameter Type Safety
565
566
```kotlin
567
// Good - explicit type handling
568
factory<Service> { params ->
569
val config: ServiceConfig = params.get()
570
val timeout: Int = params.get()
571
Service(config, timeout)
572
}
573
574
// Better - with validation
575
factory<Service> { params ->
576
val config: ServiceConfig = params.getOrNull()
577
?: throw IllegalArgumentException("ServiceConfig required")
578
val timeout: Int = params.getOrNull() ?: DEFAULT_TIMEOUT
579
Service(config, timeout)
580
}
581
```
582
583
### 4. Organize Qualifiers
584
585
```kotlin
586
object Qualifiers {
587
val PRIMARY_DB = named("primary-database")
588
val BACKUP_DB = named("backup-database")
589
val CACHE_DB = named("cache-database")
590
591
val USER_API = named("user-api-client")
592
val ADMIN_API = named("admin-api-client")
593
}
594
595
val module = module {
596
single<Database>(Qualifiers.PRIMARY_DB) { /* ... */ }
597
single<ApiClient>(Qualifiers.USER_API) { /* ... */ }
598
}
599
```
600
601
Qualifiers and parameters provide powerful mechanisms for handling complex dependency injection scenarios while maintaining clean, type-safe code. They enable flexible, context-aware dependency resolution that scales with application complexity.