Home > Software design >  Kotlin: use generic on interface level as argument type for function
Kotlin: use generic on interface level as argument type for function

Time:11-18

Is it impossible to use generic on interface level as argument type for function? I read about out and in keywords but as I understand they don't work for this case.

    interface BaseB
    open class ChildB1: BaseB
    open class ChildB2: BaseB

    abstract class BaseMapper<V: BaseB> {
        open fun test(v: V) {
            return
        }
    }

    class TestMapper1: BaseMapper<ChildB1>() {
        override fun test(v: ChildB1) {
            return
        }
    }

    class TestMapper2: BaseMapper<ChildB2>() {
        override fun test(v: ChildB2) {
            return
        }
    }

    @Test
    fun t() {
        //ERROR
        val mappers: List<BaseMapper<BaseB>> = listOf(TestMapper1(), TestMapper2())
        mappers[0].test(ChildB1())
    }

CodePudding user response:

A BaseMapper<ChildB1> is not logically a BaseMapper<BaseB>. It consumes ChildB’s, so if you passed some other implementation of Base it would cause a ClassCastException if the compiler let you do that. There is no common subtype of your two subclasses besides Nothing, so the only way to put both of these types in the same list is to make it a List<BaseMapper<in Nothing>>.

Example of why it is not logically a BaseMapper<BaseB>:

open class ChildB1: BaseB {
    fun sayHello() = println("Hello world")
}

class TestMapper1: BaseMapper<ChildB1>() {
    override fun test(v: ChildB1) {
        v.sayHello() // if v is not a ChildB1, this would be impossible
    }
}

//...

val impossibleCast: BaseMapper<BaseB> = TestMapper1()

// TestMapper1 cannot call sayHello() because it's undefined for ChildB2.
// This is impossible:
impossibleCast.test(ChildB2())

// ...so the compiler prevents you from doing the impossible cast in the first place.
  • Related