Navigation

Related Articles

Back to Latest Articles

Why Kotlin Favor Composition Over Inheritance

Discussing Kotlin Interface delegation and the shortcoming of the implementation inheritance


Baraa Abuzaid
@baraaabuzaid
Why Kotlin Favor Composition Over Inheritance

For starters what is composition and what is inheritance? A simple way to explain that is to say the Composition is when you design your object around what they do, and the Inheritance in when you design your object about what they are. Despite the fact that implementation inheritance sometime might get you into thinking that you are doing the most elegant design, it comes with a huge package. Because at the beginning of any project most of the time you really don’t have a vivid idea of exactly all classes and relation between them are going to be. And soon you start building your design around assumptions that might change at any point in the future. Making your beautiful design built upon Inheritance fall apart.
I think Mattias Petter has put it so nicely in his video composition over inheritance I really recommend watching.

Let’s continue our discussing on the inheritance shortcoming by taking a look at this example. suppose you are extending HashSet adding some sort of counting that increments whenever a new item is added to the set.

class MyHashSet:HashSet<Double>() {

    var count:Double = 0.toDouble()

    override fun add(element: Double): Boolean {
        count +=1
        return super.add(element)
    }

    override fun addAll(elements: Collection<Double>): Boolean {
        count += elements.size
        return super.addAll(elements)
    }
}
fun main(args:Array<String>){
    val myHashSet = MyHashSet();
    myHashSet.add(1.0)
    myHashSet.add(2.0)
    myHashSet.addAll(listOf<Double>(3.0,4.0));

    println("total number of the elements in the set = ${myHashSet.count} ")

    // out put :>> total number of the elements in the set = 6.0
}

Surprisingly running our MyHashSet get us a wrong number of elements count. and that’s because internally the method addAll() uses add(). resulting in add() being called twice when using addAll() and instead of getting 4 we get 6.
off course we could change how MyHashSet do counting and remove the incrementation from addAll() method. But, that’s still not a good object orientation. And the very fact that I still need to check how the class been implemented internally, reject strong point in good object orientated programming, the idea that all object should be treated as a black box and used easily without the overhead of knowing the detailed implementation.
Using compostion intead of inheritance will be a good idea to avoid such outcome. and in doing so programming language like Kotlin provides a handy features.
Kotlin does favor composition over inheritance by cutting off all the boilerplate using interface delegation.

interface IManager{
    fun plan()
    fun work()
}

open class SalesManager:IManager{
    override fun plan() {
        println(">> I do planing")
    }

    override fun work() {
        println("I work at Sales Department")
    }
}

// one line composition through interface delegation
class Supervisor (manager:IManager): IManager by manager

fun main(args:Array<String>){
    val supervisor = Supervisor(SalesManager())
    supervisor.work()
}

 

As you can see we have SalesManager class that implements IManager interface. Furthermore, we also have Supervisor class that needs to do the same work that SalesManager class do. So, instead of having duplicate code or doing inheritance, we could reach the desired result through composition. Looking at the following line
class Supervisor (manager:IManager): IManager by manager;
We can forward any calling to the method plan() or work() to the SalesManager class. All by using the byclause which indicates that manager will be stored internally in object of Supervisor and the compiler will generate all the methods of IManager that forward to manager.

Now let’s have a look at how we could achieve the same result in Java.

public interface IManager {
    void plan();
    void work();
}


public class SalesManager implements IManager {
    @Override
    public void plan() {
        System.out.println(">> I do planing");
    }

    @Override
    public void work() {
        System.out.println("I work at Sales Department");
    }
}

public class Supervisor {
    IManager manager;

    public Supervisor(IManager manager) {
        this.manager = manager;
    }

    public void work(){
        manager.work();
    }

    public void plan(){
        manager.plan();
    }
}

public class Main {
    public static void main(String[] args){
        IManager manager = new SalesManager();
        Supervisor supervisor = new Supervisor(manager);
        supervisor.work();
    }
}

Clearly, Kotlin nailed it by cutting off a lots of boilerplate. This might look trivial in this example but, imagine in a large project how Kotlin approach will be pretty handy and will get you as well into favoring composition over inheritance.

 

 

Photo by  Ricardo Gomez Angel

Show Comments (2)

Comments

  • Aung

    In java version, why Supervisior doesn’t implement IManager. Thanks in advance.

    • Article Author
    • Reply
    • Baraa Abuzaid

      Hi Aung,
      Yes, here in this example you could. but I just want to demonstrate how Kotlin Interface delegation
      would like if we are about to do it in java. since java interfaces prior to Java 8 can’t carry any default implementation.
      But, You might imagine a case where a class like SalesManager that implements IManager Interface carries lots of
      implementation details and it comes from a third-party library, and you don’t want to go through the hassle of reimplemented yourself.

      • Article Author
      • Reply

Related Articles

Kotlin

A Complete Guide To Design Patterns In Kotlin: Proxy Design Pattern

A Proxy design pattern is a structural design pattern. the idea behind is to make a proxy object that is capable of performing tasks similar to the original object. The need for...

Posted on by Baraa Abuzaid
Kotlin

A Complete Guide To Design Patterns In Kotlin: Composite Design Pattern

A composite design pattern is built upon two principles in object-oriented programming. First, decomposition by breaking a whole into parts. Then generalization which generalizing...

Posted on by Baraa Abuzaid