Core documentation

Introduction

Description

Kodein is a very useful dependency injection / retrieval container, it is very easy to use and configure.

Kodein allows you to:
  • Lazily instantiate your dependencies when needed.

  • Stop caring about dependency initialization order.

  • Easily bind classes or interfaces to their instance, provider or factory.

  • Easily debug your dependency bindings and recursions.

Kodein is a good choice because:
  • It is small, fast and optimized (makes extensive use of inline).

  • It proposes a very simple and readable declarative DSL.

  • It is not subject to type erasure (like Java).

  • It integrates nicely with Android.

  • It proposes a very kotlin-esque idiomatic API.

  • It can be used in plain Java.

Example

Kodein makes it very easy to bind a type:

Example bindings:
val kodein = Kodein {
    bind<Dice>() with provider { RandomDice(0, 5) }
    bind<DataSource>() with singleton { SqliteDS.open("path/to/file") }
}

Once bindings are declared, Kodein allows you to either inject or retrieve the dependencies for a class.

If you want your class to be unaware of dependency retrieval, then you can inject dependencies at construction:

Using Kodein’s dependency injection via construction:
class Controller(private val ds: DataSource) {
    /*...*/
}
val controller by kodein.newInstance { Controller(instance()) }

If you want your class to handle it’s dependencies by itself, then you can have it easily retrieve them:

Using Kodein’s dependency injection via construction:
class Controller(override val kodein: Kodein): KodeinAware {
    private val ds: DataSource by instance()
}

Platform compatibility & Genericity

Kodein is compatible with all platforms that the Kotlin language compiles to: JVM & compatible (Android), Javascript and all the Kotlin/Native targets.

On the JVM & compatible (Android), you need to choose to use either the erased or the generic version.
On the Javascript and Native targets, only the erased version is available.

The difference is very simple: the generic version is NOT subject to type erasure while the erased version IS.

Of course, it is a little bit more complicated!
To be able to circumvent the type erasure that’s inherent to the JVM bytecode, the generic version uses a trix that makes heavy use of reflexivity. Because the erased version does not use that trix, handling generic types in Kodein are a lot more complex to use.

  Type erasure Optimized Non-generic bindings Generic bindings

generic

immune

no

simple

simple

erased

subject

yes

simple

complex

Yes, #perfmatters. However, the humble opinion of the author is that:

  • There is a balance to be found between performance, readability, security and debuggability.

  • Optimisation is important in critical path, not everywhere.

  • Kodein is already pretty optimized ;)

  • In the vast majority of cases, using the erased version will result in no significant performance change to your application, as IoC happens once and is not a performance pitfall!

Therefore, please make sure that, using the erased version is right for your use case, before blindly using it ;). Do profile your code!

On the JVM, you might prefer the erased version if:

  • You are confident you are not binding / injecting / retrieving generic types and you are sure none of the libraries you are using are.

  • You are not using set bindings.

If you profile your code and find that injection is a performance pitfall, then it probably is instanciation: you are creating too many objects in critical paths. Reusing objects in critical paths will enhance performance both in dependency injection / retrieval and in GC!

If you are using the erased version, either by choice on the JVM, or by default on JS & Native, you should read erased version pitfalls.

Install

JVM

With Maven

Add the JCenter repository:

<repositories>
    <repository>
      <id>jcenter</id>
      <url>https://jcenter.bintray.com</url>
    </repository>
</repositories>

Then add the dependency:

<dependencies>
    <dependency>
        <groupId>org.kodein.di</groupId>
        <artifactId>kodein-di-generic-jvm</artifactId>
        <version>6.5.5</version>
    </dependency>
</dependencies>
Use kodein-di-generic-jvm or kodein-di-erased-jvm.

With Gradle

Add the JCenter repository:

buildscript {
    repositories {
        jcenter()
    }
}

Then add the dependency:

dependencies {
    implementation 'org.kodein.di:kodein-di-generic-jvm:6.5.5'
}
Use kodein-di-generic-jvm or kodein-di-erased-jvm.

JavaScript (Gradle)

Because Kodein for JavaScript is compiled as a UMD module, it can be imported:

  • In a browser:

    • as an AMD module (for example with RequireJS) (See index.html in the demo project).

    • Directly in an HTML page with a <script> tag (See index2.html in the demo project).

  • In NodeJS, as a regular CJS module.

Add the JCenter repository:

buildscript {
    repositories {
        jcenter()
    }
}

Then add the dependency:

dependencies {
    compile 'org.kodein.di:kodein-di-erased-js:6.5.5'
}

Native (Gradle)

Kodein supports the following targets:
androidArm32, androidArm64, iosArm32, iosArm64, iosX64, linuxArm32Hfp, linuxMips32, linuxMipsel32, linuxX64, macosX64, mingwX64

Kodein-DI uses the new gradle native dependency model. Because that model is experimental in gradle, it is not forward compatible with next versions of Gradle.

Add the JCenter repository:

buildscript {
    repositories {
        jcenter()
    }
}

Then add the dependency:

kotlin {
    sourceSets {
        commonMain {
            dependencies {
                implementation "org.kodein.di:kodein-di-erased:6.5.5"
            }
        }
    }
}

Bindings: Declaring dependencies

Example: initialization of a Kodein container
val kodein = Kodein {
	/* Bindings */
}

Bindings are declared inside a Kodein initialization block.

If you are using kodein-di-generic-jvm, Kodein not subject to type erasure (e.g. You can bind both a List<Int> and a List<String>).
This is NOT the case when using kodein-di-erased-jvm, kodein-erased-js or kodein-erased-native. With the erased version by default, binding List<Int> and List<String> actually means binding List<*> twice.

A binding always starts with bind<TYPE>() with.

There are different ways to declare bindings:

Tagged bindings

All bindings can be tagged to allow you to bind different instances of the same type.

Example: different Dice bindings
val kodein = Kodein {
    bind<Dice>() with ... (1)
    bind<Dice>(tag = "DnD10") with ... (2)
    bind<Dice>(tag = "DnD20") with ... (2)
}
1 Default binding (with no tag)
2 Bindings with tags ("DnD10" and "DnD20")
The tag is of type Any, it does not have to be a String.
Whether at define, at injection or at retrieval, tag should always be passed as a named argument.
Tag objects must support equality & hashcode comparison. It is therefore recommended to either use primitives (Strings, Ints, etc.) or data classes.

Provider binding

This binds a type to a provider function, which is a function that takes no arguments and returns an object of the bound type (eg. () → T).
The provided function will be called each time you need an instance of the bound type.

Example: creates a new 6 sided Dice entry each time you need one
val kodein = Kodein {
    bind<Dice>() with provider { RandomDice(6) }
}

Singleton binding

This binds a type to an instance of this type that will lazily be created at first use via a singleton function, which is a function that takes no arguments and returns an object of the bound type (eg. () → T).
Therefore, the provided function will be called only once: the first time an instance is needed.

Example: creates a DataSource singleton that will be initialized on first access
val kodein = Kodein {
    bind<DataSource>() with singleton { SqliteDS.open("path/to/file") }
}

Non-synced singleton

By definition, there can be only one instance of a singleton, which means only one instance can be constructed. To achieve this certainty, Kodein synchronizes construction. This means that, when a singleton instance is requested and not available, Kodein uses a synchronization mutex to ensure that other request to the same type will wait for this instance to be constructed.

While this behaviour is the only way to ensure the singleton’s correctness, it is also costly (due to the mutex) and degrades startup performance.

If you need to improve startup performance, if you know what you are doing, you can disable this synchronization.

Example: creates a DataSource non synced singleton
val kodein = Kodein {
    bind<DataSource>() with singleton(sync = false) { SqliteDS.open("path/to/file") }
}

Using sync = false means that:

  • There will be no construction synchronicity.

  • There may be multiple instance constructed.

  • Instance will be reused as much as possible.

Eager singleton

This is the same as a regular singleton, except that the provided function will be called as soon as the Kodein instance is created and all bindings are defined.

Example: creates a DataSource singleton that will be initialized as soon as the binding block ends
val kodein = Kodein {
    // The SQLite connection will be opened as soon as the kodein instance is ready
    bind<DataSource>() with eagerSingleton { SqliteDS.open("path/to/file") }
}

Factory binding

This binds a type to a factory function, which is a function that takes an argument of a defined type and that returns an object of the bound type (eg. (A) → T).
The provided function will be called each time you need an instance of the bound type.

Example: creates a new Dice each time you need one, according to an Int representing the number of sides
val kodein = Kodein {
    bind<Dice>() with factory { sides: Int -> RandomDice(sides) }
}

Multi-arguments factories

This multi-agrument-factories mechanism is deprecated and will be removed in version 7.0

A factory can take multiple (up to 5) arguments:

Example: creates a new Dice each time you need one, according to an Int representing the number of sides
val kodein = Kodein {
    bind<Dice>() with factory { startNumber: Int, sides: Int -> RandomDice(sides) }
}
We recommand to use data classes instead!

Regarding our users feedbacks, we find out that multi-arguments factories was difficult to use.

Thus this mechanism will be deprecate soon. So we highly recommend that you migrate your multi-args factories to simple factories by using data classes.

Example: creates a new Dice each time you need one, according to multiple parameters
data class DiceParams(val startNumber: Int, val sides: Int)

val kodein = Kodein {
    bind<Dice>() with factory { params: DiceParams -> RandomDice(params) }
}

Multiton binding

A multiton can be thought of a "singleton factory": it guarantees to always return the same object given the same argument. In other words, for a given argument, the first time a multiton is called with this argument, it will call the function to create an instance; and will always yield that same instance when called with the same argument.

Example: creates one random generator for each value
val kodein = Kodein {
    bind<RandomGenerator>() with multiton { max: Int -> SecureRandomGenerator(max) }
}

Just like a factory, a multiton can take multiple (up to 5) arguments.

non-synced multiton

Just like a singleton, a multiton synchronization can be disabled:

Example: non-synced multiton
val kodein = Kodein {
    bind<RandomGenerator>(sync = false) with multiton { max: Int -> SecureRandomGenerator(max) }
}

Referenced singleton or multiton binding

A referenced singleton is an object that is guaranteed to be single as long as a reference object can return it. A referenced multiton is an object that is guaranteed to be single for the same argument as long as a reference object can return it.

A referenced singleton or multiton needs a "reference maker" in addition to the classic construction function that determines the type of reference that will be used.

Kodein comes with three reference makers for the JVM:

JVM: Soft & weak

These are objects that are guaranteed to be single in the JVM at a given time, but not guaranteed to be single during the application lifetime. If there are no more strong references to the instances, they may be GC’d and later, re-created.

Therefore, the provided function may or may not be called multiple times during the application lifetime.

Example: creates a Cache object that will exist only once at a given time
val kodein = Kodein {
    bind<Map>() with singleton(ref = softReference) { WorldMap() } (1)
    bind<Client>() with singleton(ref = weakReference) { id -> clientFromDB(id) } (2)
}
1 Because it’s bound by a soft reference, the JVM will GC it before any OutOfMemoryException can occur.
2 Because it’s bound by a weak reference, the JVM will GC it is no more referenced.

Weak singletons use JVM’s WeakReference while soft singletons use JVM’s SoftReference.

JVM: Thread local

This is the same as the standard singleton binding, except that each thread gets a different instance. Therefore, the provided function will be called once per thread that needs the instance, the first time it is requested.

Example: creates a Cache object that will exist once per thread
val kodein = Kodein {
    bind<Cache>() with singleton(ref = threadLocal) { LRUCache(16 * 1024) }
}
Semantically, thread local singletons should use [scoped-singletons], the reason it uses a referenced singleton is because Java’s ThreadLocal acts like a reference.
Thread locals are not available in JavaScript.

Instance binding

This binds a type to an instance that already exist.

Example: a DataSource binding to an already existing instance.
val kodein = Kodein {
    bind<DataSource>() with instance(SqliteDataSource.open("path/to/file")) (1)
}
1 Instance is used with parenthesis: it is not given a function, but an instance.

Constant binding

It is often useful to bind "configuration" constants.

Constants are always tagged.
Example: two constants
val kodein = Kodein {
    constant(tag = "maxThread") with 8 (1)
    constant(tag = "serverURL") with "https://my.server.url" (1)
}
1 Note the absence of curly braces: it is not given a function, but an instance.
You should only use constant bindings for very simple types without inheritance or interface (e.g. primitive types and data classes).

Direct binding

Sometimes, it may seem overkill to specify the type to bind if you are binding the same type as you are creating.

For this use case, you can transform any bind<Type>() with …​ to bind() from …​.

Example: direct bindings
val kodein = Kodein {
    bind() from singleton { RandomDice(6) }
    bind("DnD20") from provider { RandomDice(20) }
    bind() from instance(SqliteDataSource.open("path/to/file"))
}
This should be used with care as binding a concrete class and, therefore, having concrete dependencies is an anti-pattern that later prevents modularisation and mocking / testing.
When using kodein-generic-* and binding a generic type, the bound type will be the specialized type,
e.g. bind() from singleton { listOf(1, 2, 3, 4) } registers the binding to List<Int>.
If you are using Kodein/Native, because of this bug, you need to use the uppercase version: Bind() from. This issue has been fixed and the bind() from syntax will be available to Kodein/Native as soon as Kotlin/Native 0.6 is released.

Subtypes bindings

Kodein allows you register a "subtype bindings factory". These are big words for a simple concept that’s best explained with an example:

Example: direct bindings
val kodein = Kodein {
    bind<Controller>().subtypes() with { type ->
        when (type.jvmType) { (1)
            MySpecialController::class.java -> singleton { MySpecialController() }
            else -> provider { myControllerSystem.getController(type.jvmType) }
        }
    }
}
1 As type is a TypeToken<*>, you can use .jvmType to get the JVM type (e.g. Class or ParameterizedType).

In essence, bind<Whatever>().subtypes() with { type → binding } allows you to register, in Kodein, a binding factory that will be called for subtypes of the provided type.

Transitive dependencies

With those lazily instantiated dependencies, a dependency (very) often needs another dependency. Such classes can have their dependencies passed to their constructor. Thanks to Kotlin’s killer type inference engine, Kodein makes retrieval of transitive dependencies really easy.

Example: a class that needs transitive dependencies
class Dice(private val random: Random, private val sides: Int) {
/*...*/
}

It is really easy to bind this RandomDice with its transitive dependencies, by simply using instance() or instance(tag).

Example: bindings of a Dice and of its transitive dependencies
val kodein = Kodein {
    bind<Dice>() with singleton { Dice(instance(), instance(tag = "max")) } (1)

    bind<Random>() with provider { SecureRandom() } (2)
    constant(tag "max") with 5 (2)
}
1 Binding of Dice. It gets its transitive dependencies by using instance() and instance(tag).
2 Bindings of Dice transitive dependencies.
The order in which the bindings are declared has no importance whatsoever.

The binding functions are in the same environment as the newInstance function described in the dependency injection section. You can read it to learn more about the instance, provider and factory functions available to the function.

Transitive factory dependencies

Maybe you need a dependency to use one of its functions to create the bound type.

Example: using a DataSource to create a Connection.
val kodein = Kodein {
    bind<DataSource>() with singleton { MySQLDataSource() }
    bind<Connection>() with provider { instance<DataSource>().openConnection() } (1)
}
1 Using a DataSource as a transitive factory dependency.

Being responsible for its own retrieval

If the bound class is KodeinAware, you can pass the kodein object to the class so it can itself use the Kodein container to retrieve its own dependencies.

Example: bindings of Manager that is responsible for retrieving its own dependencies
val kodein = Kodein {
    bind<Manager>() with singleton { ManagerImpl(kodein) } (1)
}
1 ManagerImpl is given a Kodein instance.

Bindings separation

Modules

Definition

Kodein allows you to export your bindings in modules. It is very useful to have separate modules defining their own bindings instead of having only one central binding definition. A module is an object that you can construct the exact same way as you construct a Kodein instance.

Example: a simple module
val apiModule = Kodein.Module(name = "API") {
    bind<API>() with singleton { APIImpl() }
    /* other bindings */
}

Then, in your Kodein binding block:

Example: imports the module
val kodein = Kodein {
    import(apiModule)
    /* other bindings */
}
Modules are definitions, they will re-declare their bindings in each Kodein instance you use. If you create a module that defines a singleton and import that module into two different Kodein instances, then the singleton object will exist twice: once in each Kodein instance.

Name uniqueness

Each module name should only be imported once.

If a second module with the name of an already imported module is imported, then Kodein will fail.

However, you cannot always ensure that every module name is unique: you may need to import modules that are defined outside of your code. Kodein offers two ways to mitigate that:

  1. Rename a module:
    Use when you are importing a module whose name already exists.

    Example: imports a renamed module
    val kodein = Kodein {
        import(apiModule.copy(name = "otherAPI"))
    }
  2. Add a prefix to modules imported by a module:
    Use when a module imported by another module uses a names which already exists.

    Example: imports a module with a prefix for sub-modules
    val kodein = Kodein {
        import(apiModule.copy(prefix = "otherAPI-"))
    }

Import once

You may define a module which you know depends on another module, so it would be great to import that dependency inside the module that has the dependency. However, each module can only be imported once, so if every module that depends on another module imports it, Kodein will fail at the second module that imports it.

To support this, Kodein offers importOnce: it imports the module if no module with that name was previously imported.

Example: importing a module only once
val appModule = Kodein.Module {
    importOnce(apiModule)
}

Extension (composition)

Kodein allows you to create a new Kodein instance by extending an existing one.

Example: extends an already existing Kodein instance
val subKodein = Kodein {
    extend(appKodein)
    /* other bindings */
}
This preserves bindings, meaning that a singleton in the parent Kodein will continue to exist only once. Both parent and child Kodein objects will give the same instance.

Overriding

By default, overriding a binding is not allowed in Kodein. That is because accidentally binding twice the same (class,tag) to different instances/providers/factories can cause real headaches to debug.

However, when intended, it can be really interesting to override a binding, especially when creating a testing environment. You can override an existing binding by specifying explicitly that it is an override.

Example: binds twice the same type, the second time explitly specifying an override
val kodein = Kodein {
    bind<API>() with singleton { APIImpl() }
    /* ... */
    bind<API>(overrides = true) with singleton { OtherAPIImpl() }
}

By default, modules are not allowed to override, even explicitly. You can allow a module to override some of your bindings when you import it (the same goes for extension):

Example: imports a module and giving it the right to override existing bindings.
val kodein = Kodein {
    /* ... */
    import(testEnvModule, allowOverride = true)
}
The bindings in the module still need to specify explicitly the overrides.

Sometimes, you just want to define bindings without knowing if you are actually overriding a previous binding or defining a new. Those cases should be rare and you should know what you are doing.

Example: declaring a module in which each binding may or may not override existing bindings.
val testModule = Kodein.Module(name = "test", allowSilentOverride = true) {
    bind<EmailClient>() with singleton { MockEmailClient() } (1)
}
1 Maybe adding a new binding, maybe overriding an existing one, who knows?

If you want to access an instance retrieved by the overridden binding, you can use overriddenInstance. This is useful if you want to "enhance" a binding (for example, using the decorator pattern).

Example: declaring a module in which each binding may or may not override existing bindings.
val testModule = Kodein.Module(name = "test") {
    bind<Logger>(overrides = true) with singleton { FileLoggerWrapper("path/to/file", overriddenInstance()) } (1)
}
1 overriddenInstance() will return the Logger instance retrieved by the overridden binding.

Overridden access from parent

Let’s consider the following code :

Example: Mixing overriding & extension
val parent = Kodein {
    bind<Foo>() with provider { Foo1() }
    bind<Bar>() with singleton { Bar(foo = instance<Foo>()) }
}

val child = Kodein {
    extend(parent)
    bind<Foo>(overrides = true) with provider { Foo2() }
}

val foo = child.instance<Bar>().foo

In this example, the foo variable will be of type Foo1. Because the Bar binding is a singleton and is declared in the parent Kodein, it does not have access to bindings declared in child. In this example, both parent.instance<Bar>().foo and child.instance<Bar>().foo will yield a Foo1 object.

This is because Bar is bound to a singleton, the first access would define the container used (parent or child). If the singleton were initialized by child, then a subsequent access from parent would yeild a Bar with a reference to a Foo2, which is not supposed to exist in parent.
By default, all bindings that do not cache instances (basically all bindings but singleton and multiton) are copied by default into the new container, and therefore have access to the bindings & overrides of this new container.

If you want the Bar singleton to have access to the overridden Foo binding, you need to copy it into the child container.

Example: Copying the bar binding into the child container
val child = Kodein {
    extend(parent, copy = Copy {
        copy the binding<Bar>() (1)
    })
    bind<Foo>(overrides = true) with provider { Foo2() }
}
Copying a binding means that it will exists once more. Therefore, a copied singleton will no longer be unique and have TWO instances, one managed by each binding (the original and the copied).

If the binding you need to copy is bound by a context (such as a scoped singleton), you need to specify it:

Example: Copying a tagged scoped singleton
val parent = Kodein {
    bind<Session>(tag = "req") with scoped(requestScope).singleton { context.session() }
}

val child = Kodein {
    extend(parent, copy = Copy {
        copy the binding<Session>() with scope(requestScope) and tag("req")
    })
    bind<Foo>(overrides = true) with provider { Foo2() }
}
You can use the context<>(), scope() and tag() functions to specialise your binding copies.

You can also copy all bindings that matches a particular definition :

Example: Copying all that matches
val child = Kodein {
    extend(parent, copy = Copy {
        copy all binding<String>() (1)
        copy all scope(requestScope) (2)
    })
}
1 Will copy all bindings for a String, with or without a context, scope, tag or argument.
2 Will copy all bindings that are scoped inside a RequestScope.

Finally, you can simply copy all bindings:

Example: Copying all
val child = Kodein {
    extend(parent, copy = Copy.All)
}

Or you can decide that none are copied (if you do want existing bindings to have access to new bindings):

Example: Copying none
val child = Kodein {
    extend(parent, copy = Copy.None)
}

Dependency injection & retrieval

Example bindings that are used throughout the chapter:
val kodein = Kodein {
    bind<Dice>() with factory { sides: Int -> RandomDice(sides) }
    bind<DataSource>() with singleton { SqliteDS.open("path/to/file") }
    bind<Random>() with provider { SecureRandom() }
    bind<FileAccess>() with factory { path: String, mode: Int -> FileAccess.open(path, mode) }
    constant("answer") with "fourty-two"
}

Retrieval rules

When retrieving a dependency, the following rules apply:
  • A dependency bound with a provider, an instance, a singleton, an eagerSingleton, or a constant can be retrieved:

    • as a provider method: () → T

    • as an instance: T

  • A dependency bound with a factory or a multiton can only be retrieved as a factory method: (A) → T.

    • as a factory method: (A) → T

    • as a provider method: () → T if the argument A is provided at retrieval.

    • as an instance: T if the argument A is provided at retrieval.

Injection & Retrieval

When dependencies are injected, the class is provided its dependencies at construction.
When dependencies are retrieved, the class is responsible for getting its own dependencies.

Using dependency injection is a bit more cumbersome, but your classes are "pure": they are unaware of the dependency container. Using dependency retrieval is easier (and allows more tooling), but it does binds your classes to the Kodein API.

Finally, in retrieval, everything is lazy by default, while there can be no lazy-loading using injection.

If you are developing a library, then you probably should use dependency injection, to avoid forcing the users of your library to use Kodein as well.
If you are developing an application, then you should consider using dependency retrieval, as it is easier to use and provides more tooling.

Base methods

Whether you are using dependency injection or retrieval, the same 3 methods will be available with the same name and parameters (but not return type).
These methods are:

  • instance() if you need an instance: T.

  • provider() if you need a provider: () → T.

  • factory() if you need an instance: (A) → T.

All three methods can take a tag argument.

The tag argument should always be named.

Example: Using the named tag argument.
instance(tag = "whatever").

Injection

To use dependency injection,

  1. Declare your dependencies in the constructor of your classes.

  2. Use Kodein’s newInstance method to create an object of such class.

Simple case

Example: a MainController class with a 2 dependencies constructor.
class MainController(val ds: DataSource, val rnd: Random) { /*...*/ }
Example: Creating a MainController by injecting its dependencies.
val controller by kodein.newInstance { MainController(instance(), instance(tag = "whatever")) } (1)
1 Note the use of the instance function that will inject the correct dependency.
When injecting a type that was not bound, a Kodein.NotFoundException will be thrown.

If you are not sure (or simply do not know) if the type has been bound, you can use *OrNull methods.

Multi-arguments factories

When injecting a value that was bound with a multi-argument factory, the arguments must be wrapped inside a data class:

Example: Creating a FileController by injecting a multi-argument bound dependency.
data class ControllerParams(val path: String, val timeout: Int)
val controller by kodein.newInstance { FileController(instance(args = ControllerParams("path/to/file", 0))) }

Currying factories

You can retrieve a provider or an instance from a factory bound type by using the arg parameter (this is called currying).

Example: a RollController class with a constructor dependency bound to a factory.
class RollController(val dice: Dice) { /*...*/ }
Example: Creating a RollController by injecting its dependency.
val controller by kodein.newInstance { RollController(instance(arg = 6)) }

Note that if you want to bind a factory with multiple argument, you need to use a data class to pass multiple arguments:

Example: Creating a multi-argument RollController by injecting its dependency.
data class Params(val arg1: Int, val arg2: Int)
val controller by kodein.newInstance { RollController(instance(arg = Params(60, 6))) }
The arg argument should always be named.

Defining context

When retrieving, you sometimes need to manually define a context (for example, when retrieving a scoped singleton). For this, you can use the on method:

Example: Setting a global context.
val controller by kodein.on(context = myContext).newInstance { OtherController(instance(arg = 6), instance()) }
The context argument should always be named.

Sometimes, the context is not available directly at construction. When that happens, you can define a lazy context that will be accessed only when needed.

Example: Setting a global context.
val controller by kodein.on { requireActivity() } .newInstance { OtherController(instance(arg = 6), instance()) }

Retrieval: the Kodein container

everything is lazy by default!

In the next few sections, we will be describing dependency retrieval. As you might have guessed by the title of this section, everything, in dependency retrieval, is lazy by default.

This allows:

  • Dependencies to be retrieved only when they are actually needed.

  • "Out of context" classes such as Android Activities to access their dependencies once their contexts have been initialized.

If you want "direct" retrieval, well, there’s a section named direct retrieval, how about that!

Kodein methods

You can retrieve a bound type via a Kodein instance.

Example: retrieving bindings
val diceFactory: (Int) -> Dice by kodein.factory()
val dataSource: DataSource by kodein.instance()
val randomProvider: () -> Random by kodein.provider()
val answerConstant: String by kodein.instance(tag = "answer")

Note the use of the by. Kodein uses delegated properties to enable:

  • Lazy loading

  • Accessing the receiver

When using a provider function (() → T), whether this function will give each time a new instance or the same depends on the binding.
When asking for a type that was not bound, a Kodein.NotFoundException will be thrown.

If you are not sure (or simply do not know) if the type has been bound, you can use *OrNull methods.

Example: retrieving bindings that may not have been bound
val diceFactory: ((Int) -> Dice)? by kodein.factoryOrNull()
val dataSource: DataSource? by kodein.instanceOrNull()
val randomProvider: (() -> Random)? by kodein.providerOrNull()
val answerConstant: String? by kodein.instanceOrNull(tag = "answer")

Constants

If you bound constants, you can easily retrieve them with the constant method if the name of the property matches the tag:

Example: retrieving a constant
val answer: String by kodein.constant()

Named bindings

If you used tagged bindings, if the tag is a String and the property name matches the tag, instead of passing it as argument, you can use named:

Example: retrieving a named binding
val answer: String by kodein.named.instance()

Multi-arguments factories

When retrieving a value that was bound with a multi-argument factory, the arguments must be wrapped inside a data class:

Example: Creating a MainController by injecting a multi-argument bound dependency.
data class FileParams(val path: String, val maxSize: Int)
val fileAccess: FileAccess by kodein.instance(args = FileParams("/path/to/file", 0))
Factory retrieval

Instead of retrieving a value, you can retrieve a factory, that can call as much as you need.

Example: Retrieving factory.
val f1: (Int) -> Int by kodein.factory() (1)
1 retrieving a factory that takes 1 argument (Int) and return an Int

Currying factories

You can retrieve a provider or an instance from a factory bound type by using the arg parameter (this is called currying).

Example: currying factories
val sixSideDiceProvider: () -> Dice by kodein.provider(arg = 6)
val twentySideDice: Dice by kodein.instance(arg = 20)

Note that if you bound a factory with multiple arguments, you need to use a data class to pass multiple arguments:

Example: Creating a multi-argument Dice by injecting its dependency.
data class DiceParams(val startNumber: Int, val sides: Int)
val sixtyToSixtySixDice: Dice by kodein.instance(arg = DiceParams(60, 6)) (1)
1 Bonus points if you can say the variable name 5 times in less than 5 seconds ;)
The arg argument should always be named.

Defining context

Whether you are using a scoped singleton/multiton or using a context in the target binding, you may need to specify a context.

Example: Getting a Session after setting the Request context.
val session: Session by kodein.on(context = request).instance()

If you retrieve multiple dependencies all using the same context, you can create a new Kodein object with the context set:

Example: creating a Kodein object with the Request context.
val reqKodein = kodein.on(context = request)
val session: Session by reqKodein.instance()
The context argument should always be named.
Using a global context does not forces you to use only bindings that are declared with this type of context. Because the default context is Any?, all non-contexted bindings will still be available with a global context set.

Using a Trigger

There is a mechanism that allows you to decide when dependencies are actually retrieved if you want them to be retrieved at a particular time and not at first access. This mechanism is called a Trigger.

Example: using a trigger.
val trigger = KodeinTrigger()
val dice: Dice by kodein.on(trigger = trigger).instance()
/*...*/
trigger.trigger() (1)
1 Retrieval happens now.

You can, of course, assign multiple properties to the same trigger. You can also create a Kodein object that has a given trigger by default:

Example: creating a Kodein object with a trigger.
val trigger = KodeinTrigger()
val injectKodein = kodein.on(trigger = trigger)
val dice: Dice by injectKodein.instance()
/*...*/
trigger.trigger()
The trigger argument should always be named.
A trigger allows you to "force" retrieval. However, retrieval can still happen before inject() is called if the variable is accessed.

Lazy access

Kodein proposes a LazyKodein object that allows you to lazily access the Kodein object only when needed. This is useful if:

  • You need to defined a lazily retrieved dependency before having access to a Kodein container.

  • You don’t know if you’ll ever need to access a Kodein object.

For this, you can use a LazyKodein:

Example: Using a LazyKodein.
val kodein = LazyKodein { /* access to a kodein instance */ }
val ds: DataSource by kodein.instance()
/*...*/
dice.roll() (1)
1 Only then will the Kodein instance will itself be retrieved.

Note that you can also lazily create a Kodein object so that the bindings definition function will only be called when the first retrieved property is needed:

Example: Using a lazy Kodein.
val kodein by Kodein.lazy {
    bind<Env>() with instance(Env.getInstance())
}
val env: Env by kodein.instance()
/*...*/
env.doSomething() (1)
1 Only then will the Kodein instance will itself be created, and the bindings definition function ran.

Late init

Kodein proposes a LateInitKodein that allows you to define a Kodein object after some lazy retrieval:

Example: Using a LateInitKodein.
val kodein = LateInitKodein()
val env: Env by kodein.instance()
/*...*/
kodein.baseKodein = /* access to a kodein instance */ (1)
/*...*/
env.doSomething() (2)
1 Setting the real Kodein object.
2 If this was run before setting kodein.baseKodein, an UninitializedPropertyAccessException would be thrown.

All matches

Kodein allows you to retrieve all instances that matches a given type:

Example: all instances of Foo.
val instances: List<Foo> by kodein.allInstances() (1)
1 Will return all instances that are for bindings of sub-classes of Foo
Of course, allProviders and allFactories are also provided ;)

Retrieval: being KodeinAware

Simple retrieval

You can have classes that implement the interface KodeinAware.
Doing so has the benefit of getting a simpler syntax for retrieval.

Example: a KodeinAware class
class MyManager(override val kodein: Kodein) : KodeinAware {
    private val diceFactory: ((Int) -> Dice)? by factoryOrNull()
    private val dataSource: DataSource? by instanceOrNull()
    private val randomProvider: (() -> Random)? by providerOrNull()
    private val answerConstant: String? by instanceOrNull(tag = "answer")
    private val sixSideDiceProvider: () -> Dice by kodein.provider(arg = 6)
    private val twentySideDice: Dice by kodein.instance(arg = 20)
}

All methods that are available to the Kodein container are available to a KodeinAware class.

Class global context

In a KodeinAware class, to define a context that’s valid for the entire class, you can simply override the kodeinContext property:

Example: a KodeinAware class with a context
class MyManager(override val kodein: Kodein) : KodeinAware {
    override val kodeinContext = kcontext(whatever) (1)
    /*...*/
}
1 Note the use of the kcontext function that creates a KodeinContext with the given value.
Using a global context does not forces you to use only bindings that are declared with this type of context. Because the default context is Any?, all non-contexted bindings will still be available with a global context set.

Sometimes, the context is not available directly at construction. When that happens, you can define a lazy context that will be accessed only when needed.

Example: a KodeinAware class with a context
class MyManager(override val kodein: Kodein) : KodeinAware {
    override val kodeinContext = kcontext { requireActivity }
    /*...*/
}

Class global trigger

If you want to have all dependency properties retrieved at once, you can use a class global trigger. Simply override the kodeinTrigger property:

Example: a KodeinAware class with a trigger
class MyManager(override val kodein: Kodein) : KodeinAware {
    override val kodeinTrigger = KodeinTrigger()
    val ds: DataSource by instance()
    /*...*/
    fun onReady() {
        kodeinTrigger.trigger() (1)
    }
}
1 Retrieval of all dependencies happens now.

Lazy access

Some classes (such as Android Activities) do not have access to a Kodein instance at the time of construction, but only later when they have been properly connected to their environment (Android context).
Because Kodein is lazy by default, this does not cause any issue: simply have the kodein property be lazy by itself:

Example: an Activity class with a lazy-loaded kodein
class MyActivity : Activity(), KodeinAware {
    override val kodein by lazy { (applicationContext as MyApplication).kodein }
    val ds: DataSource by instance() (1)
}
1 Because ds is lazily retrieved, access to the kodein property will only happen at first retrieval.
There is an official module to ease the use of Kodein in Android, you can read more about it on the dedicated document.

Lateinit

Because everything is lazy and, in a KodeinAware class, the Kodein object is not accessed until needed, you can easily declare the kodein field as lateinit.

Example: an Activity class with a lateinit kodein
class MyActivity : Activity(), KodeinAware {
    override val lateinit kodein: Kodein
    val ds: DataSource by instance() (1)
    override fun onCreate(savedInstanceState: Bundle?) {
        kodein = (applicationContext as MyApplication).kodein
    }
}
1 Because ds is lazily retrieved, access to the kodein property will only happen at first retrieval.

All matches

Kodein allows you to retrieve all instances that matches a given type:

Example: all instances of Foo.
val instances: List<Foo> = dkodein.allInstances() (1)
1 Will return all instances that are for bindings of sub-classes of Foo
Of course, allProviders and allFactories are also provided ;)

Retrieval: Direct

If you don’t want to use delegated properties, Kodein has you covered. Most of the features available to Kodein are available to DKodein (D is for Direct). DKodein allows you to directly get a new instance or dependency.

However, because it is direct, DKodein does NOT feature:

  • Laziness: the instance/provider/factory is fetched at call time.

  • Receiver awareness: receiver is defined by the Kotlin’s delegated properties mechanism.

Example: using a DKodein
val dk = kodein.direct

val ds: Datasource = dk.instance()

val controller = dk.newInstance { MainController(instance(), instance(tag = "whatever")) }

If you only plan to use direct access, you can define your main kodein object to be a DKodein:

Example: using a DKodein
val kodein = Kodein.direct { (1)
        /* bindings */
    }

<1>: Note the .direct.

Being DKodeinAware

Much like Kodein offers KodeinAware, DKodein offers DKodeinAware

Example: a DKodeinAware class
class MyManager(override val dkodein: DKodein) : DKodeinAware {
    private val diceFactory: ((Int) -> Dice)? = factoryOrNull()
    private val dataSource: DataSource? = instanceOrNull()
    private val randomProvider: (() -> Random)? = providerOrNull()
    private val answerConstant: String? = instanceOrNull(tag = "answer")
    private val sixSideDiceProvider: () -> Dice = kodein.provider(arg = 6)
    private val twentySideDice: Dice = kodein.instance(arg = 20)
}

In Java

While Kodein does not allow you to declare modules or dependencies in Java, it does allow you to retrieve dependencies via DKodein. Simply give the DKodein instance to your Java classes, use Kodein in Java with the TT static function:

Example: using Kodein in Java
import static org.kodein.di.TypesKt.TT;

public class JavaClass {
    private final Function1<Integer, Dice> diceFactory;
    private final Datasource dataSource;
    private final Function0<Random> randomProvider;
    private final String answerConstant;

    public JavaClass(DKodein kodein) {
        diceFactory = kodein.Factory(TT(Integer.class), TT(Dice.class), null);
        dataSource = kodein.Instance(TT(Datasource.class), null);
        randomProvider = kodein.Provider(TT(Random.class), null);
        answerConstant = kodein.Instance(TT(String.class), "answer");
    }}

Remember that Java is subject to type erasure. Therefore, if you registered a generic Class binding such as bind<List<String>>(), in order to retrieve it you have to use TypeReference to circumvent Java’s type erasure.

Example: using TypeReference in Java
class JavaClass {
    private final List<String> list;

    public JavaClass(TKodein kodein) {
        list = kodein.Instance(TT(new TypeReference<List<String>>() {}), null);
    }
}

Error messages

By default, Kodein error messages contains the classes simple names (e.g. View), which makes it easily readable.
If you want the error to contain classes full names (e.g. com.company.app.UserController.View), you can set fullDescriptionOnError:

Example: showing qualified names in errors
val kodein = Kodein {
    fullDescriptionOnError = true
}

If you are using multiple Kodein instances, you can set the default value fullDescriptionOnError for all subsequently created Kodein instances:

Example: showing qualified names in all kodein instances errors
Kodein.defaultFullDescriptionOnError = true
Kodein.defaultFullDescriptionOnError must be set before creating a Kodein instance.

Using the environment

Binding functions have access to the environment where the bound type is retrieved to be able to create it accordingly.

Context

This environment is represented as a context variable.

The context is an object that is explicitly defined by the programmer for this retrieval or the receiving object when none is explicitely defined.

There are two very important differences between a tag and a context:

  • The tag instance identifies the binding but can not be used in the binding function.

  • The context type identifies the binding and it’s instance can be used in the binding function.

There are also two very important differences between a factory argument and a context:

  • The context is defined before retrieving the binding function while the factory argument is the last known variable.

  • A context is usually global to an entire class while a factory argument is local to a retrieval.

When in doubt, use a factory with an argument instead of a provider with a context.
Example: binding in a context
val kodein = Kodein {
    bind<Writer>() with contexted<Request>.provider { context.response.writer } (1)
}
1 note that context is already of type Request.

Scope

Kodein provides only 1 scope by default, but:

  • It is easy to create your own scopes.

  • All kodein-framework-* modules provide more scopes that are specific to the target framework.

Scopes are derived from a context variable. They allow a singleton or multiton objects to exist multiple times in different contexts.
They are of type Scope<C> where C is the context type.

Think, for example, of a session object inside a web server. We can say that there can be only one user per session, and therefore define a User singleton scoped in a session. Therefore, the provided function will be called once per session.

Example: binding a User in a Session scope.
val kodein = Kodein {
    bind<User>() with scoped(SessionScope).singleton { UserData(session.userId) } (1)
}
1 note that SessionScope does not really exist, it is an example.

In this example, SessionScope is of type Scope<Session>, so to access this binding, the user will either have retrieve it inside the session object or explicitly define a Session context:

Example: binding in a context
val user by kodein.on(session).instance()
Please read the Scope creation section if you want to create your own scopes.

Scope closeable

By default, a Singleton or a Multiton value will never expire. However, the purpose of a Scope is to handle the lifecycle of a long lived value. Therefore, it is possible for a scoped Singleton or Multiton value to expire (most of the time because the scope itself expires). For example, in android’s ActivityRetainedScope, scoped values will only live the duration of the activity.

If a value implements ScopeCloseable, it’s close function will be called when the value is removed from the scope (or when the scope itself expires).

The ScopeCloseable.close method will only be called:

  • By scopes that explicitely support that feature (not all scopes do, all scopes provided by the Kodein Framework do except WeakContextScope).

  • If the value does not use WeakRef or SoftRef references.
    If the value does, the close method may or may not be called (it will be called if the reference has not expired).

JVM references in scopes

Yes, you can…​

Example: JVM scoped weak references.
val kodein = Kodein {
    bind<User>() with scoped(requestScope).singleton(ref = weakReference) {
        instance<DataSource>().createUser(context.session.id)
    } (1)
}

Weak Context Scope

Kodein provides the WeakContextScope scope. This is a particular scope, as the context it holds on are weak references.

WeakContextScope is NOT compatible with ScopeCloseable.

You can use this scope when it makes sense to have a scope on a context that is held by the system for the duration of its life cycle.

Example: controller scoped to an Activity with WeakContextScope.
val kodein = Kodein {
    bind<Controller>() with scoped(WeakContextScope.of<Activity>()).singleton { ControllerImpl(context) } (1)
}
1 context is of type Activity because we are using the WeakContextScope.of<Activity>().

WeakContextScope.of will always return the same scope, which you should never clean!

If you need a compartimentalized scope which you can clean, you can create a new WeakContextScope:

Example: creating a WeakContextScope.
val activityScope = WeakContextScope<Activity>()

Context translators

Let’s get back to the web server example. There is one session per user, so we have bound a User singleton inside a Session scope. As each Request is associated with a Session, you can register a context translator that will make any binding that needs a Session context work with a Request context:

Example:
val kodein = Kodein {
    bind<User>() with scoped(SessionScope).singleton { UserData(session.userId) }

    registerContextTranslator { r: Request -> r.session }
}

This allows you to retrieve a User instance:

  • When there is a global Request context:

    Example: retriving with a global context
    class MyController(override val kodein: Kodein, request: Request): KodeinAware {
        override val kodeinContext = kcontext(request)
    
        val user: User by instance()
    }
  • When the retrieval happens on a Request itself:

    Example: retriving with a global context
    class MySpecialRequest(override val kodein: Kodein): Request(), KodeinAware {
        val user: User by instance()
    }

Context finder

A context finder is a similar to context translator, except that it gets the context from a global context.

For example, if you are in a thread-based server where each request is assigned a thread (are people still doing those?!?), you could get the session from a global:

Example:
val kodein = Kodein {
    bind<User>() with scoped(SessionScope).singleton { UserData(session.userId) }

    registerContextFinder { ThreadLocalSession.get() }
}

This allows to access a User object wihout specifying a context.

Having an other type of context declared will not block from using a context finder.

Scope creation

Scoped singletons/multitons are bound to a context and live while that context exists.

To define a scope that can contain scoped singletons or multitons, you must define an object that implements the Scope interface. This object will be responsible for providing a ScopeRegistry according to a context. It should always return the same ScopeRegistry when given the same context object. A standard way of doing so is to use the userData property of the context, if it has one, or else to use a WeakHashMap<C, ScopeRegistry>.

Example: a simple session scope
object SessionScope : Scope<Session> { (1)
    override fun getRegistry(context: Session): ScopeRegistry =
            context.userData as? ScopeRegistry
                ?: StandardScopeRegistry().also { context.userData = it } (2)
}
1 The scope’s context type is Session.
2 Creates a ScopeRegistry and attach it to the Session if there is none.
Scope providers should also provide standard context translators.
In this example, we should provide, along with sessionScope a module providing the Request to Session context translator.

Scope registry

The ScopeRegistry is responsible for holding value instances. It is also responsible for calling the close methods on object that are ScopeCloseable when they are removed from the registry.

To have your scope compatible with ScopeCloseable values, make sure to clean the registry when the scope expires.

There are two standard implementations of ScopeRegistry:

StandardScopeRegistry

This is the "classic" expected ScopeRegistry behaviour.

SingleItemScopeRegistry

This is a particular ScopeRegistry implementation : it will only hold one item and replace the held item if the binding asks for an instance of another binding.

This means that a Multiton scoped with a Scope that uses a SingleItemScopeRegistry will actually hold only one instance: the one corresponding to the last argument.

You should NOT use this registry unless you know exactly WHAT you are doing, and WHY you are doing it.

Sub-scopes

You can define a scope to be defined inside another scope. This means that when the parent scope clears, so does all of its subscopes.

Example: a simple session scope
val requestScope = object : SubScope<Request, Session>(sessionScope) {
    override fun getParentContext(context: Request) = context.session
}

In this simple example, when the session expires, then all of its associates request scoped values also expire.

Multi-binding

Kodein 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 kodein = Kodein {
    bind() from setBinding<Configuration>() (1)

    bind<Configuration>().inSet() with provider { FooConfiguration() } (2)
    bind<Configuration>().inSet() with singleton { BarConfiguration() } (2)
}
1 Creating a set binding of Configuration.
2 Binding multiple Configuration implementations.

You can:

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

  • Add bindings to the same set in different modules, provided that the set has been declared first.

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.
val kodein = Kodein {
    bind() from argSetBinding<Query, Result>()

    bind<Result>().inSet() with factory { q: Query -> Foo.query(q) }
    bind<Result>().inSet() with multiton { q: Query -> Bar.query(q) }
}

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 with the generic version.
val configurations: Set<Configuration> by kodein.instance()

if you are using the erased version, you need to retrieve thusly:

Example retrieving set of Configuration with the erased version.
val configurations: Set<Configuration> by kodein.Instance(erasedSet())

In a map

Kodein 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 kodein = Kodein {
    bind() from setBinding<ConfigurationEntry>()

    bind<ConfigurationEntry>().inSet() with factory { "foo" to FooConfiguration() }
    bind<ConfigurationEntry>().inSet() with multiton { "bar" to BarConfiguration() }
}

Finally, retrieve the map:

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

Debugging

Print bindings

You can easily print bindings with println(kodein.container.tree.bindings.description).

Here’s an example of what this prints:

An example of kodein.container.tree.bindings.description:
        bind<Dice>() with factory { Int -> RandomDice }
        bind<DataSource>() with singleton { SQLiteDataSource }
        bind<Random>() with provider { SecureRandom }
        bind<String>(tag = "answer") with instance ( Int )

As you can see, it’s really easy to understand which type with which tag is bound to which implementation inside which scope.

Description prints type names in a "kotlin-esque" way. Because Kodein does not depend on kotlin-reflect, it uses java Type objects that do not contain nullability information. As such, the type display does not include nullability. Still, it’s easier to read List<*> than List<? extends Object>.

Recursive dependency loop

When it detects a recursive dependency, Kodein will throw a Kodein.DependencyLoopException. The message of the exception explains how the loop happened.

An example of recursive dependency loop:
Kodein$DependencyLoopException: Dependency recursion:
     bind<Database>()
    ╔╩>bind<User>() (1)
    ║  ╚>bind<Repository>(tag = "users") (2)
    ║    ╚>bind<Database>() (3)
    ╚══════╝
1 Database depends on User
2 User depends on Repository with the tag "users"
3 Repository with the tag "users" depends on Database, we have found the dependency loop!.

OnReady callbacks

You can define callbacks to be called once the kodein instance is ready and all bindings are defined. This can be useful to do some "starting" jobs.

Example: registering a callback at binding time
val appModule = Kodein.Module(name = "app") {
    import(engineModule)
    onReady {
        val engine = instance<Engine>()
        instance<Logger>().info("Starting engine version ${engine.version}")
        engine.start()
    }
}

External Source

An external source is responsible for providing an answer when Kodein cannot find one.

When Kodein cannot find a binding for the required type/argument/context, then it calls the external source.

Example: an external source
val kodein = Kodein {
    externalSource = ExternalSource { key ->
        when (key.type.jvmType) { (1)
            Whatever::class.java -> when (key.argType.jvmType) { (2)
                Unit::class.java -> when (key.tag) { (3)
                    "user" -> externalFactory { existingInstance } (4)
                    null -> externalFactory { Whatever("default-value") } (4)
                    else -> null (6)
                }
                String::class.java -> when (key.tag) { (3)
                    null -> externalFactory { Whatever(it as String) } (5)
                    else -> null (6)
                }
                else -> null (6)
            }
            else -> null (6)
        }
    }
}
1 The type that is required
2 The argument type (Unit if no argument)
3 The tag (null if no tag)
4 You can return an existing instance or a new one
5 The argument has been checked to be a String, so it can be safely casted
6 Return null if the external source has no answer

The externalSource property takes an ExternalSource instance, which is a SAM interface that can be implemented by a lambda with the ExternalSource { } constructor. This ExternalSource is called every time a new Key is asked but not found. The Key itself contains information about the binding that was asked but not found.

The ExternalSource will be called only once per unknown key.

The ExternalSource must return a function (which you can easily create with the externalFactory utility function) that takes an Any? argument and returns the instance. This function will be called every time an instance is requested. Note that if no argument is provided, the argument to the lambda will be Unit.

Erased version pitfalls

The type erasure problem

When using the generic version on the JVM, Kodein is immune to type erasure, meaning that bind<List<String>>() and bind<List<Int>>() will represent two different bindings.
Similarly, kodein.instance<List<String>>() and kodein.instance<List<Int>>() will yield two different list.

To be erasure immune, the generic JVM version relies heavily on the generic function, which is known to be slow.

To improve performance, you can use the erased JVM Kodein version, which is faster, but do suffer from type erasure!

Furthermore, on Javascript and Native platforms, there’s no choice: erased is the only version available!

Using generic and erased function forms

Each kodein function that handles a type exists in two form: as inline (lowercased first letter) and as regular function (uppercased first letter).
For example, the kodein.instance function also exists as kodein.Instance.

The uppercase functions need TypeToken parameters that define the type being bound / retrieved and maybe the factory’s argument.
You can easily use these functions with the generic or erased functions:

Example: using the erased function
val ds: DataSource by kodein.Instance(erased())

By default, all inline functions are aliases to their uppercase counterparts using the generic function.
For example, the kodein.instance() function is an alias to kodein.Instance(generic())

So, when you know that you inject a type that is not generic, you can use kodein.Instance(erased()).

Erased parameterized generic types

When using the erased function or using erased by default (either by choice on the JVM or by necessity elsewhere), you cannot represent a generic type.
For example, erased<Set<String>> will yield a TypeToken representing Set<*>.

Kodein provides a way to represent a generic type in an erased way:

Example: generic type tokens, using erased
erasedComp1<Set<String>, String>()                         // Represents a Set<String>
erasedComp2<Map<Int, String>, Int, String>()               // Represents a Map<Int, String>
erasedComp3<Triple<Int, String, Int>, Int, String, Int>()  // Represents a Triple<Int, String, Int>
The type parameter themselves are erased, meaning that you cannot represent a multi-level generic type. You can, however, construct your own CompositeTypeToken to represent such a type.

Bind the same type to different factories

Yeah, when I said earlier that "you can have multiple bindings of the same type, as long as they are bound with different tags", I lied. Because each binding is actually a factory, the binding tuples are not ([BindType], [Tag]) but actually ([ContextType], [BindType], [ArgType], [Tag]) (note that providers and singletons are bound as ([BindType], Unit, [Tag])). This means that any combination of these three information can be bound to it’s own factory, which in turns means that you can bind the same type without tagging to different factories.

Please be cautious when using this knowledge, as other less thorough readers may get confused with it.

Hack the container!

The KodeinContainer is the sacred Kodein object that contains all bindings and is responsible for retrieval. You can access it with kodein.container. In it, each Binding is bound to a Kodein.Key.

In fact, all Kodein functions are proxies to this container API.

When defining bindings, in the Kodein.Builder, you can access the container property to bind factories to a Kodein.Key or a Kodein.Bind.

Tag vs context vs argument

  Binding identification accessible by the binding itself* accessible by the binding function

tag

instance

no

no

context

type

yes

yes

argument

type

no

yes

Explore bindings

You can access a copy of the bindings map with kodein.container.bindings.
From this Map<Kodein.Key, Factory<*, *>>, you can explore all bindings, their keys and factories.

Community

Contribute

Contributions are very welcome and greatly appreciated! The great majority of pull requests are eventually merged.

To contribute, simply fork the project on Github, fix whatever is iching you, and submit a pull request!

I am sure that this documentation contains typos, inaccuracies and languages error (English is not my mother tongue). If you feel like enhancing this document, you can propose a pull request that modifies the documentation documents. (Documentation is auto-generated from those).

Let’s talk!

You’ve read so far?! You’re awesome!
Why don’t you drop by the Kodein Slack channel on Kotlin’s Slack group?