Here’s another use-case. I have class that implements an abstract game element:
abstract class Element {
var posX = 0.0f
var posY = 0.0f
var posZ = 0.0f
fun move(x: Float, y: Float, z: Float) { /* ... */ }
protected fun doSomething() { /* ... */ }
// and much more
}
The Element is part of a module that’s the game’s engine. The game has an element for the player, which is part of the concrete game implementation, not part of the engine, i.e. it’s in a different module.
class PlayerImpl: Element(), IPlayerExtras {
override var phi = 0.0f
// ...
}
The engine provides an interface for the player’s extra values.
interface IPlayerExtras {
val phi: Float
}
But here’s the problem: The engine can’t easily deal with player elements since in Kotlin I cannot declare a variable like val player: Element & IPlayerExtras.
// engine
abstract class App {
abstract val player: IPlayerExtras
fun movePlayer() {
// cannot access player.posX without type assertion, which is inefficient
if (player is Element) { /* ... */ }
}
}
// game implementation
class AppImpl: App {
override val player = PlayerImpl()
}
To solve the problem, I have two choices. Either I introduce a new level in the inheritance chain like this: Element < AbstrPlayer < PlayerImpl. I don’t like this, because I’ve seen too much messy code caused by deeply nested inheritance chains (especially in C++ this seems to be a common problem). I basically want to avoid inheritance chains deeper than one level.
The other solution is what I call the Java way: Make an interface IElement that repeats all the members of Element, then derive an IPlayer from IElement, IPlayerExtras, and then derive PlayerImpl from IPlayer. I consider this poor style because of two reasons: First, having to repeat all the members of Element in IElement is bloated and Kotlin-unlike. Second, having both an Element and an IElement leads to confusion about which one to use, which I’ve also seen in the past with some Java libraries.
In my opinion, a typealias with an intersection type would be the cleanest and least verbose solution.
typealias PlayerElement = Element & IPlayerExtras