Наследование реализаций интерфейсов на Kotlin

Мне нужно реализовать два интерфейса. У каждого интерфейса будет по несколько наследников. А потом нужно создать интерфейс, который будет состоять из наследников предыдущих интерфейсов, причем неважно каких. Сразу на примере - есть интерфейсы кузов машины и двигатель, у каждого есть по значению:

// кузов
interface Carcase {
   val aerodynamics: Float 

   fun getAD(){return aerodynamics}
}

// двигатель
interface Engine {
   val power: Int

   fun getP(){return power}
}

У каждого есть по несколько реализаций:

class Lada: Carcase {
    override val aerodynamics: Float = 1.0f
}
class Volvo: Carcase {
    override val aerodynamics: Float = 1.5f
}

class V1: Engine {
    override val power: Int = 10
}
class V3: Engine {
    override val power: Int = 20
}
class V5: Engine {
    override val power: Int = 30
}

А теперь мне надо реализовать интерфейс или класс, который будет собирать из двух реализаций одну, что то вроде этого:

// правильно ли это и вообще возможно ли - не знаю
interface Car<S,T>: Carcase, Engine {
    fun getSpeed{
        return( S.getAD() * T.getP() )  //?????
    }
}

В итоге я должен писать в основном коде что то вроде:

val car1 = Car<Lada, V1>
val car2 = Car<Volvo, V5>

И должно работать:

val s1 = car1.getSpeed() // 1.0f * 10
val s2 = car2.getSpeed() // 1.5f * 30

Вопрос: как правильно реализовать такое наследование? Помогут и прямой ответ ответ на вопрос, и ссылки на книги (с указанием раздела, что смотреть). Так как я сам даже не понимаю, в какую сторону тут надо гуглить.


Ответы (1 шт):

Автор решения: Eugene Krivenja

Могу предложить что-то подобное (композиция вместо наследования).

class Car<C, E>(private val case : C, private val engine: E) where C:Carcase, E:Engine {
    fun getSpeed() : Float {
        return( case.getAD() * engine.getP() )
    }
}

Использование

val car1 = Car(Lada(), V1())
val car2 = Car(Volvo(), V5())

println("Lada: ${car1.getSpeed()}")  // Lada: 10.0
println("Volvo: ${car2.getSpeed()}") // Volvo: 45.0

Бонус. Можно использовать inline reified функцию для быстрого "производства" машин.

inline fun <reified C: Carcase, reified E: Engine> produce(): Car<C, E> {
    val case = C::class.constructors.first().call()
    val engine = E::class.constructors.first().call()

    return Car(case, engine)
}

Использование

val car1 = produce<Lada, V1>()
val car2 = produce<Volvo, V5>()

Функция написана с условием, что у классов корпуса и двигателя есть только один конструктор без параметров!

→ Ссылка