How kotlin delegation is useful?

It is extremely useful for creating decorators and for object composition. Joshua Bloch in Effective Java, 2nd Edition, Item 16 'Favor Composition Over Inheritance' shows a good example: inheritance is easy-to-break, and decorators are not.

Inheritance:

class LoggingList<E> : ArrayList<E>() {

    override fun add(e: E): Boolean {
        println("added $e")
        return super.add(e)
    }

    override fun addAll(e: Collection<E>): Boolean {
        println("added all: $e")
        return super.addAll(e) // oops! Calls [add] internally.
    }

}

Delegation:

class LoggingList<E>(delegate: MutableList<E>) : MutableList<E> by delegate {

    override fun add(e: E): Boolean {
        println("added $e")
        return delegate.add(e)
    }

    override fun addAll(e: Collection<E>): Boolean {
        println("added all: $e")
        return delegate.addAll(e) // all OK
        // it calls [delegate]'s [add] internally, not ours
    }

}

Also remember that you're not restricted to just one delegate. Kotlin's way of implementing delegation is similar to traits implementation in languages like Groovy. You can compose different functionality via delegates. Kotlin's way can also be considered more powerful because you can "plug in" different implementations too.

interface Marks {
  fun printMarks()
}

class StdMarks() : Marks {
  override fun printMarks() { println("printed marks") }
}

class CsvMarks() : Marks {
  override fun printMarks() { println("printed csv marks") }
}

interface Totals {
  fun printTotals()
}

class StdTotals : Totals {
  override fun printTotals() { println("calculated and printed totals") }
}

class CheatTotals : Totals {
  override fun printTotals() { println("calculated and printed higher totals") }
}

class Student(val studentId: Int, marks: Marks, totals: Totals) 
  : Marks by marks, Totals by totals

fun main(args:Array<String>) {
  val student = Student(1,StdMarks(), StdTotals())
  student.printMarks()
  student.printTotals()
  val cheater = Student(1,CsvMarks(), CheatTotals())
  cheater.printMarks()
  cheater.printTotals()
}

Output:

printed marks
calculated and printed totals
printed csv marks
calculated and printed higher totals

You can't do this with inheritance.

Tags:

Kotlin