ref – https://medium.com/@Joseph82/interface-delegation-in-kotlin-1404dfcd9167
Interface delegation, in Kotlin, is a simple yet powerful technique that allows a class to delegate the actual implementation of the interfaces, to separate objects.
Usually, we have a class implement a function which satisfies the required interface function like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// declare our interface interface EngineType { val type: String } // wants our component FiatBravo to conform to EngineType // we then override interface function 'type' class FiatBravo : EngineType { override val type: String get() = "Electric" } // we then create an instance of FiatBravo, and then get its engine type. val fiatBravo = FiatBravo() // Engine type: Electric println("Engine type: ${fiatBravo.type}") |
So our FiatBraov is directly implementing the abstract property defined in the interface EngineType.
But note that it’s 2020, and many other vehicles are probably going to use the Electric, instead of Diesel. We don’t want to have very car component re-write this Electric Engine type over and over.
Let’s use SteeringWheel as an example.
We want to create a SteeringWheel object that every car component just conform to it and use, and not have to implement it individually.
Let’s first create an interface like so:
1 2 3 4 |
interface SteeringControl { fun turnLeft() fun turnRight() } |
This tells us that whatever component conforms to this SteeringControl interface must at least implement turnLeft and turnRight.
We then create an object SteeringWheel and conform to SteeringControl. In our SteeringWheel component, we implement those abstract functions set by our interface SteeringControl.
1 2 3 4 5 6 7 8 9 |
object SteeringWheel : SteeringControl { override fun turnLeft() { println("Rotate steering wheel counterclockwise") } override fun turnRight() { println("Rotate steering wheel clockwise") } } |
Now, back in our FiatBravo, we must conform to interface SteeringControl because we’re a car. But we’d rather someone else do the implementation for us because we don’t want to be bothered with this. Thus, we use the by keyword to say that SteeringWheel is a component that conforms to SteeringControl, and we want it to implement it for us.
In other words, interface SteeringControls is to be implemented by SteeringWheel.
1 2 3 4 5 6 7 |
class FiatBravo : EngineType, SteeringControl by SteeringWheel { override val type: String get() = "Electric" } |
Now the output would be:
Turn handlebar to left
Turn handlebar to right
Engine type: Electric
So as you can see, we can over-ride abstract interface methods, and we can also have other components do it for us.
Finally, notice that our SteeringWheel is an object. We can also use a class to represent the blueprint of a component that does the implementing for us. And then just instantiate it
Let’s use Tires as an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
interface Tires { val brandName: String val circumference: Array<Int> } class GoodYear : Tires { override val circumference: Array<Int> get() = arrayOf<Int>(18, 22, 26) override val brandName: String get()="Good Year" } class FiatBravo : EngineType, SteeringControl by SteeringWheel, Tires by GoodYear() { override val type: String get() = "Electric" } fun main(args: Array<String>) { val fiatBravo = FiatBravo() fiatBravo.turnLeft() fiatBravo.turnRight() println("Engine type: ${fiatBravo.type}") } |