Delegation is a design pattern that combines object composition and inheritance. Basically, it is a mechanism where one object delegates actions to another.
The Delegation pattern is a great alternative to typical inheritance of classes. Delegation allows a certain class to be derived from another one or to implement an interface. However, under the hood, the derived class is not a subclass of the base class but the composition is used instead to provide the properties of the base class to the derived one. Whenever a request to the properties of the base class part is made, it is being redirected to a delegated object. This is comparable to subclasses deferring a request to parent classes. However, delegation not only allows us to achieve the same code reusability as inheritance does, it's also much more powerful and customizable. Kotlin makes the Delegation pattern even more impressive because it provides a built-in support for declaring delegates using the by
keyword.
To see it in action, let's take a look how we would achieve delegation in Java:
public interface Drivable { void drive(); } public class Car implements Drivable { @Override public void drive() { System.out.println("Driving a car"); } } public class Vehicle implements Drivable { private Car car; public Vehicle(Car car) { this.car = car; } @Override public void drive() { car.drive(); } } public void driveVehicle() { Car car = new Car(); Vehicle vehicle = new Vehicle(car); vehicle.drive(); // with delegation, car.drive() get's called }
In this example, we have the Drivable
interface and two classes that implement it. The Vehicle
class doesn't have its own drive
functionality; rather, it delegates the drive
function to the Car
object.
If we were to write the same example in Kotlin, it would look like this:
interface Drivable { fun drive() } class Car : Drivable { override fun drive() { println("Driving a car") } } class Vehicle(car: Car) : Drivable by car
You can see how the Vehicle
class doesn't have any of the code that the Java version had. That is because Kotlin has built-in support for delegation. Notice how in the class header of the Vehicle
class, after the Drivable
interface, comes the by
keyword and the name of the object that we are delegating this interface to. This is all that is needed for the compiler to produce more or less equivalent bytecode to the Java example.
In our example, we used interfaces, but delegation in Kotlin can also be used for classes, that is, you can delegate the inheritance of a class to another object.
Delegation also works with multiple interfaces. This is how you would have a class that delegates two interfaces to other objects:
interface Rideable { fun ride() } interface Chargable { fun charge() } class Battery : Chargable { override fun charge() { println("Charging") } } class Bike : Rideable { override fun ride() { println("Riding a bike") } } class ChargableBike(bike: Bike, battery: Battery): Rideable by bike, Chargable by battery