Search code examples
kotlinnullpointerexceptionkodein

NullPointerException inside Kodein


I'm trying out Kotlin with Kodein and in my current project I'm getting a NPE inside Kodein and I'm don't know why.

I have some data classes and matching repositories which deliver a list of them:

data class Cat(val name: String)

data class Dog(val name: String)

interface Repository<T> {
    val all: List<T>
}

interface CatRepository : Repository<Cat>

interface DogRepository : Repository<Dog>

The implementations of these repositories are currently backed by a master class:

data class AnimalData(val cats: List<Cat>, val dogs: List<Dog>)

I created an abstract base class for the repositories:

abstract class AnimalDataRepository<T>(override val kodein: Kodein) : Repository<T>, KodeinAware {
    private val animalData: AnimalData by instance()
    abstract val property: (AnimalData) -> List<T>
    override val all: List<T> = animalData.let(property)
}

So that the repository implementations look like this:

class CatRepositoryImpl(override val kodein: Kodein) : CatRepository, AnimalDataRepository<Cat>(kodein) {
    override val property = AnimalData::cats
}

Setting this up and running with:

fun main() {
    val kodein = Kodein {
        bind<AnimalData>() with singleton { AnimalData(listOf(Cat("Tigger")), listOf(Dog("Rover"))) }
        bind<CatRepository>() with singleton { CatRepositoryImpl(kodein) }
    }

    val catRepository: CatRepository by kodein.instance()
    println(catRepository.all)
}

leads to a NPE inside Kotlin:

Exception in thread "main" java.lang.NullPointerException
    at org.kodein.di.KodeinAwareKt$Instance$1.invoke(KodeinAware.kt:176)
    at org.kodein.di.KodeinAwareKt$Instance$1.invoke(KodeinAware.kt)
    at org.kodein.di.KodeinProperty$provideDelegate$1.invoke(properties.kt:42)
    at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
    at AnimalDataRepository.getAnimalData(KodeinExample.kt)
    at AnimalDataRepository.<init>(KodeinExample.kt:27)
    at CatRepositoryImpl.<init>(KodeinExample.kt:30)
    at KodeinExampleKt$main$kodein$1$2.invoke(KodeinExample.kt:40)
    at KodeinExampleKt$main$kodein$1$2.invoke(KodeinExample.kt)
    at org.kodein.di.bindings.Singleton$getFactory$1$1$1.invoke(standardBindings.kt:130)
    ...

I'm not clear why this happens. It has something to do with the use of the "property" mapping lamba in AnimalDataRepository, because when I don't use that it works fine.

Complete code as gist: https://gist.github.com/RoToRa/65d664d2d7497ddbf851a1be019f631d


Solution

  • this is because in your class AnimalDataRepository you have defined:

    override val all: List<T> = animalData.let(property)

    While Kodein is working lazily, thus all is defined before, that's why animalData is null. However, you can fix this by doing:

    override val all: List<T> by lazy { animalData.let(property) }