Kodein-DI

Kodein-DI allows multi bindings via a binding set.

In a Set

Binding in a Set

To have multiple bindings in a set, you need to:

  • Declare that you are using a set binding for a particular bound type.

  • Add bindings to the set.

Example creating a set of Configuration bindings.
val di = DI {
    bindSet<Configuration> { // (1)
        add { provider{ FooConfiguration() } } // (2)
        bind { singleton { BarConfiguration() } } // (3)
    }
}
  1. Creating a set binding of Configuration.

  2. adds a Configuration binding implementation using the convenience method.

  3. adds a Configuration binding and attaches it to the DI container.

Note

You can:

  • Use different binding types (such as provider or singleton) in the same set.

  • Add bindings to the same set in different modules if the set has been declared first.

Convenience Methods

Kodein-DI provides two types of convenience methods for adding bindings to sets:

add* Methods

The add* methods add bindings only to the set:

Example using add* convenience methods.
val di = DI {
    bindSet<Configuration> {
        addSingleton { FooConfiguration() }   // (1)
        addProvider { BarConfiguration() }    // (2)
        addInstance(existingConfig)           // (3)
    }
}
  1. Adds a singleton binding to the set (equivalent to add { singleton { FooConfiguration() } }).

  2. Adds a provider binding to the set (equivalent to add { provider { BarConfiguration() } }).

  3. Adds an instance binding to the set (equivalent to add { instance(existingConfig) }).

bind* Methods

The bind* methods add bindings to both the set AND the DI container, allowing retrieval by tag:

Example using bind* convenience methods with tags.
val di = DI {
    bindSet<Configuration> {
        bindSingleton(tag = "foo") { FooConfiguration() }      // (1)
        bindProvider(tag = "bar") { BarConfiguration() }       // (2)
        bindInstance(tag = "existing", instance = existingConfig) // (3)
    }
}

// Retrieve from set
val allConfigs: Set<Configuration> by di.instance()

// Also retrieve individually by tag
val fooConfig: Configuration by di.instance(tag = "foo")
val barConfig: Configuration by di.instance(tag = "bar")
  1. Adds a singleton to the set AND registers it in the container with tag "foo".

  2. Adds a provider to the set AND registers it in the container with tag "bar".

  3. Adds an instance to the set AND registers it in the container with tag "existing".

Tip

Use add* methods when you only need set bindings. Use bind* methods when you want both set membership and individual retrieval by tag.

Example creating a set of Configuration bindings and populates it from modules.
val module1 by DI.Module {
    inBindSet<Configuration> {
        addProvider { FooConfiguration() } // (2)
        addSingleton { BarConfiguration() } // (2)
    }
}
val di = DI {
    bindSet<Configuration>() // (1)
    importAll(module1)
}
  1. Creating a set binding of Configuration.

  2. add multiple Configuration binding implementation using convenience methods.

You can also bind multiple bindings with arguments (such as factory or multiton) in a set as long as all bindings share the same argument type.

Example creating a set of Result bindings with add* methods.
val di = DI {
    bindArgSet<Query, Result> { // (1)
        addFactory { q: Query -> Foo.query(q) } // (2)
        addMultiton { q: Query -> Bar.query(q) } // (3)
    }
}
  1. Creating an argument set binding of Result for arguments of type Query.

  2. Adds a factory binding using convenience method (equivalent to add { factory { q: Query → Foo.query(q) } }).

  3. Adds a multiton binding using convenience method (equivalent to add { multiton { q: Query → Bar.query(q) } }).

Example using bind* methods with argument sets for tagged retrieval.
val di = DI {
    bindArgSet<Query, Result> {
        bindFactory(tag = "foo") { q: Query -> Foo.query(q) }     // (1)
        bindMultiton(tag = "bar") { q: Query -> Bar.query(q) }    // (2)
    }
}

// Retrieve from set
val results: Set<Result> by di.instance(arg = Query("SELECT * FROM USER;"))

// Also retrieve individually by tag
val fooFactory: (Query) -> Result by di.factory(tag = "foo")
val barMultiton: (Query) -> Result by di.factory(tag = "bar")
  1. Adds a factory to the set AND registers it in the container with tag "foo".

  2. Adds a multiton to the set AND registers it in the container with tag "bar".

Retrieving from a Set

Note that the type being bound is Set<T>, not T.
Therefore, you need to retrieve a Set:

Example retrieving set of Configuration.
val configurations: Set<Configuration> by di.instance()

To retrieve a Set with argument, thus going through a factory or a multiton, you need to pass the needed argument to the instance function.

Example retrieving set of Result with Query argument.
val result: Set<Result> by di.instance(arg = Query("SELECT * FROM USER;"))

In a map

Kodein-DI does not directly support map multi-binding. However, it is very easy to create a binding map by using a binding set.

First, create the following primitive:

Example of the type alias for a map multi-binding as Map<String, Configuration>.
typealias ConfigurationEntry = Pair<String, Configuration>
typealias ConfigurationEntries = Set<ConfigurationEntry>

Then, bind with keys:

Example binding as in a map multibinding.
val di = DI {
    bindSet<ConfigurationEntry> {
        add { singleton { "foo" to FooConfiguration() } }
        add { provider { "bar" to BarConfiguration() } }
    }
}

Finally, retrieve the map:

Example retrieving a map multibinding.
val configurations by di.instance<ConfigurationEntries>().toMap()