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

Abstract classes


So far, so good. Our bakery looks good. However, we have a problem with our current model. Let's look at the following code:

fun main(args: Array<String>) {
    val anyGood = BakeryGood("Generic flavour")
}

We can instantiate the BakeryGood class directly, which is too generic. To correct this situation, we can mark BakeryGood as abstract:

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

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

An abstract class is a class designed solely to be extended. An abstract class can't be instantiated, which fixes our problem.

What makes abstract different from open?

Both modifiers let us extend a class, but open lets us instantiate while abstract does not.

Now that we can't instantiate, our name() method in the BakeryGood class isn't that useful anymore, and all our subclasses, except for CinnamonRoll, override it anyway (CinnamonRoll relays on the Roll implementation):

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

  abstract fun name(): String 
}

A method marked as abstract doesn't have a body, just the signature declaration (a method signature is a way to identify a method). In Kotlin, a signature is composed of the method's name, its number, the type of parameters, and the return type.

Any class that extends BakeryGood directly must override the name() method. The technical term for overriding an abstract method is implement and, from now on, we will use it. So, the Cupcake class implements the name() method (Kotlin doesn't have a keyword for method implementation; both cases, method implementation, and method overriding, use the keyword override).

Let's introduce a new class, Customer; a bakery needs customers anyway:

class Customer(val name: String) {
  fun eats(food: BakeryGood) {
    println("$name is eating... ${food.eat()}")
  }
}

fun main(args: Array<String>) {
    val myDonut = Donut("Custard", "Powdered sugar")
    val mario = Customer("Mario")
mario.eats(myDonut)
}

The eats(food: BakeryGood) method takes a BakeryGood parameter, so any instance of any class that extends the BakeryGood parameter, it doesn't matter how many hierarchy levels. Just remember that we can instantiate BakeryGood directly.

What happens if we want a simple BakeryGood? For example, testing.

There is an alternative, an anonymous subclass:

fun main(args: Array<String>) {
    val mario = Customer("Mario")

    mario.eats(object : BakeryGood("TEST_1") {
        override fun name(): String {
            return "TEST_2"
        }
    })
}

A new keyword is introduced here, object. Later on, we'll cover object in more detail, but for now, it is enough to know that this is an object expression. An object expression defines an instance of an anonymous class that extends a type.

In our example, the object expression (technically, the anonymous class) must override the name() method and pass a value as the parameter for the BakeryGood constructor, exactly as a standard class would do.

Remember that an object expression is an instance, so it can be used to declare values:

val food: BakeryGood = object : BakeryGood("TEST_1") { 
  override fun name(): String { 
    return "TEST_2" 
  } 
} 

mario.eats(food)