Book Image

Functional Kotlin

Book Image

Functional Kotlin

Overview of this book

Functional programming makes your application faster, improves performance, and increases your productivity. Kotlin supports many of the popular and advanced functional features of functional languages. This book will cover the A-Z of functional programming in Kotlin. This book bridges the language gap for Kotlin developers by showing you how to create and consume functional constructs in Kotlin. We also bridge the domain gap by showing how functional constructs can be applied in business scenarios. We’ll take you through lambdas, pattern matching, immutability, and help you develop a deep understanding of the concepts and practices of functional programming. If you want learn to address problems using Recursion, Koltin has support for it as well. You’ll also learn how to use the funKtionale library to perform currying and lazy programming and more. Finally, you’ll learn functional design patterns and techniques that will make you a better programmer.By the end of the book, you will be more confident in your functional programming skills and will be able to apply them while programming in Kotlin.
Table of Contents (22 chapters)
Title Page
Copyright and Credits
Dedication
Packt Upsell
Contributors
Preface
Index

Inheritance


As we continue modelling our domain in Kotlin, we realize that specific objects are quite similar. If we go back to our HR example, an employee and a contractor are quite similar; both have a name, a date of birth, and so on; they also have some differences. For example, a contractor has a daily rate, while an employee has a monthly salary. It is obvious that they are similar—both of them are people; people is a superset where both contractor and employee belong. As such, both have their own specific features that make them different enough to be classified into different subsets.

This is what inheritance is all about, there are groups and subgroups and there are relationships between them. In an inheritance hierarchy, if you go up in the hierarchy, you will see more general features and behaviors, and if you go down, you will see more specific ones. A burrito and a microprocessor are both objects, but they have very different purposes and uses. 

Let's introduce a new Biscuit class:

class Biscuit(val flavour: String) { 
  fun eat(): String { 
    return "nom, nom, nom... delicious $flavour biscuit" 
  } 
}

Again, this class looks almost exactly same as Cupcake. We could refactor these classes to reduce code duplication:

open class BakeryGood(val flavour: String) { 
  fun eat(): String { 
    return "nom, nom, nom... delicious $flavour bakery good" 
  } 
} 

class Cupcake(flavour: String): BakeryGood(flavour) 
class Biscuit(flavour: String): BakeryGood(flavour)

We introduced a new BakeryGood class, with the shared behavior and state of both Cupcake and Biscuit classes and we made both classes extend BakeryGood. By doing so, Cupcake (and Biscuit) has an is-a relationship with BakeryGood now; on the other hand, BakeryGood is the Cupcake class's super or parent class.

Note that BakeryGood is marked as open. This means that we specifically design this class to be extended. In Kotlin, you can't extend a class that isn't open.

The process of moving common behaviors and states to a parent class is called generalisation. Let's have a look at the following code:

fun main(args: Array<String>) {
    val myBlueberryCupcake: BakeryGood = Cupcake("Blueberry")
    println(myBlueberryCupcake.eat())
}

Let's try out our new code:

Bummer, not what we were expecting. We need to refract it more:

open class BakeryGood(val flavour: String) { 
  fun eat(): String { 
    return "nom, nom, nom... delicious $flavour ${name()}" 
  } 

  open fun name(): String { 
    return "bakery good" 
  } 
} 

class Cupcake(flavour: String): BakeryGood(flavour) { 
  override fun name(): String { 
    return "cupcake" 
  } 
} 

class Biscuit(flavour: String): BakeryGood(flavour) { 
  override fun name(): String { 
    return "biscuit" 
  } 
}

It works! Let's have a look at theoutput:

We declared a new method, name(); it should be marked as open, because we designed it to be optionally altered in its subclasses.

Modifying a method's definition on a subclass is called override and that is why the name() method in both subclasses is marked as override.

The process of extending classes and overriding behavior in a hierarchy is called specialisation.

Note

Rule of thumb Put general states and behaviors at the top of the hierarchy (generalisation), and specific states and behaviors in subclasses (specialisation).

Now, we can have more bakery goods! Let's have a look at the following code:

open class Roll(flavour: String): BakeryGood(flavour) { 
  override fun name(): String { 
    return "roll" 
  } 
} 

class CinnamonRoll: Roll("Cinnamon")

Subclasses can be extended too. They just need to be marked asopen:

open class Donut(flavour: String, val topping: String) : BakeryGood(flavour)
{
    override fun name(): String {
        return "donut with $topping topping"
    }
}

fun main(args: Array<String>) {
    val myDonut = Donut("Custard", "Powdered sugar")
    println(myDonut.eat())
}

We can also create classes with more properties and methods.