InsertKoinIO / koin

Koin - a pragmatic lightweight dependency injection framework for Kotlin & Kotlin Multiplatform
https://insert-koin.io
Apache License 2.0
8.76k stars 695 forks source link

Using bind with generic interfaces does not work correctry #1883

Open shmaltorhbooks opened 1 month ago

shmaltorhbooks commented 1 month ago

Describe the bug When I trying to bind multiple instances to different generic interfaces I<T>, koin returns all instances of I ignoring generic type <T>

Koin module and version: koin-core:3.5.6

Snippet or Sample project to help reproduce

interface SomeHandler<T> {
    fun handle(value: T)
}

class StringsHandler: SomeHandler<String> {
    override fun handle(value: String) = println(value)
}

class LocalDateTimeHandler: SomeHandler<LocalDateTime> {
    override fun handle(value: LocalDateTime) = println(value.format(DateTimeFormatter.ISO_DATE_TIME))
}
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.koinApplication
import org.koin.dsl.module
import org.koin.core.module.dsl.bind
import java.time.LocalDateTime
import kotlin.test.assertTrue
import kotlin.test.*

class ConstructorDLSBindingTest {
    @Test
    fun test_bind_with_generic_interface() {
        val koin = koinApplication {
            modules(module {
                singleOf(::StringsHandler) { bind<SomeHandler<String>>() }
                singleOf(::LocalDateTimeHandler) { bind<SomeHandler<LocalDateTime>>() }
            })
        }.koin

        val textHandlers: List<SomeHandler<String>> = koin.getAll<SomeHandler<String>>()
        val timeHandlers: List<SomeHandler<LocalDateTime>> = koin.getAll<SomeHandler<LocalDateTime>>()

        // fail, actual size is 2
        assertTrue(textHandlers.size == 1)
        // this will throw ClassCastException
        // java.lang.String cannot be cast to java.time.LocalDateTime
        textHandlers.map { it.handle("test") }

        // the same, size == 2, ClassCastException java.time.LocalDateTime cannot be cast to java.lang.String
        assertTrue(timeHandlers.size == 1)
        timeHandlers.map { it.handle(LocalDateTime.now()) }
    }
}
WebTiger89 commented 4 weeks ago

Same issue here.

    val appModule = module {
        singleOf(::MovieRepository)
        singleOf(::UpdateMovieUseCase) { bind<UseCase<List<MovieDomainModel>>>() }
        singleOf(::EditMovieUseCase) { bind<UseCase<EditMovieDomainModel>>() }
    }
    // This does not work
    val updateUseCase = koinInject<UseCase<List<MovieDomainModel>>>()
    // This works
    //val updateUseCase = koinInject<UpdateMovieUseCase>()
    val editUseCase = koinInject<UseCase<EditMovieDomainModel>>()

    println(updateUseCase)
    println(editUseCase)

Will both print 6478-6478 System.out I com.example.composetest.EditMovieUseCase@554e934 6478-6478 System.out I com.example.composetest.EditMovieUseCase@554e934

The issue is when not the implementation is requested but rather the abstraction (interface).