Kotlin Data Access Object

Even the most trivial of computer programs work with data. In many cases, the user will wish to save data and reuse it between one run of the program and a later session. Since different systems handle data differently, the user may need to save data in multiple formats for compatibility purposes. The program should know how to not only persist data but also read data from a variety of supported formats.

Let’s consider a word processes. When we write a document in a word processor, we may normally save documents in the word processor’s default file format. Later on, we may wish to share the document with another coworker or friend, but they have a different word processor than what we are using. The solution may be to use the word processor’s export feature to convert our document from the word processor’s default format to the file format our friend’s word processor is using.

Converting data isn’t just an issue for desktop programs either. We may have a web application deployed that may store data in an RBDMS but have to export the same data to XML or JSON. Likewise, it’s very likely a web application may need to consume JSON and export data as a CSV format. Clearly, there is plenty of need for an application to be flexible about file formats.

That means we should not tie the persistent mechanism to a data structure. It might seem to go against the OOP principle of encapsulation where each object is responsible for its own data but think about the implications of such a design. If we write the persistence logic into a class, the class is tied to a particular file format. What happens when the class needs to read and write to a different file format? We could add additional persistence mechanisms to the class, but eventually, the class becomes bloated as we add more and more persistence schemes to it.

Another approach might be to define abstract methods for saving and retrieving data, but this is equally as bad of an approach. For one thing, are we really going to create an entire class hierarchy just for reading and writing data? Furthermore, we have no means with which to enforce a certain storage mechanism since all instances of such a class would resolve their storage logic using virtual methods. Finally, we would most likely need to convert instances of one subclass to another subclass in order to switch between storage mechanisms, which could send us down the rabbit hole of deep cloning objects.

If you think about it, storing and retrieving data is a completely separate concern of the application. A data object should never end up being responsible for its own persistence. That needs to be the concern of a completely different class or set of classes. Thus we end up with the data access pattern.

The data access pattern relies on a transfer object to hold the data. The transfer object is passed between the view (which is normally the UI) and the source of the data. Kotlin has a special data class that we can use for this specific purpose. Let’s have a look at an example.

/**
 * This a model class that is persisted to disk in this program
 */
data class Cook(val fName : String,
                val lName : String,
                val position : String,
                val age : Int) : Serializable

Koltin data classes get a built-in implementation of hashCode(), equals(), and toString(). We are free to add additional methods to them if needed. We also have this class implementing Serializable for a later demonstration. The important part to note about the Cook class is that it does not handle its own persistence. That is done by another class that is specific for this purpose.

Once we have our transfer object, we can begin by defining our persistence classes. Since we plan on using different formats for reading and writing data, we should use an interface as an abstraction point.

/**
 * This interface describes the kinds of Data Access Operations available
 */
interface CookDao {
    fun save(c : Cook, outStream: OutputStream)

    fun read(inStream: InputStream) : Cook
}

Our CookDao interface defines two methods. The first method, save(), takes a Cook object and an OuputStream. The Cook object is, of course, the transfer object. As for the OutputStream, it’s best to use OutputStream rather than File because it allows this Dao class to work with non-file streams such as network sockets. The DAO class should be concerned with how the object is persisted and retrieved, but it should not be coupled to a particular source. This keeps it flexible and allows us to send and store data on File Systems, Network Sockets, Http Responses, etc.

The same principle holds true for the read() function as well. This method takes an InputStream, the reciprocal class for OutputStream, and returns a Cook object. This allows us to create a Cook from any input stream. Once again, the source of the InputStream isn’t important to the class, only that it contains the data needed to create a Cook object.

Now that we have our interface, let’s look out our implementations. At first, our application wishes to store Cooks as Serialized Java Objects. Let’s create a version of CookDao that accomplishes this task.

/**
 * Implementation of CookDao that uses JVM serialization
 */
class SerializedCookDao : CookDao{
    override fun save(c: Cook, outStream: OutputStream) {
        val o = ObjectOutputStream(outStream)
        outStream.use {
            o.writeObject(c)
        }
    }

    override fun read(inStream: InputStream) : Cook{
        val i = ObjectInputStream(inStream)
        inStream.use {
            return i.readObject() as Cook
        }
    }
}

The SerializedCookDao implements CookDao and stores Cook as a Serialized Java object. When we wish to restore the Cook, we use the readObject() method found on ObjectInputStream to restore the Cook. Either way, the application at large isn’t concerned about how cook is persisted and restored.

Later on, we decide to support command separated values format or CSV. We don’t need to change any client code to accomplish such as task. All we need to do is define another class that implements CookDao.

/**
 * Implementation of CookDao that converts Cooks to a CSV file
 */
class CsvCookDao : CookDao {

    //Private extension function on Cook for creating
    //CSV files
    private fun Cook.toCSV() : String {
        return "${this.fName},${this.lName},${this.age},${this.position}"
    }

    //Private function to convert a CSV line to a Cook
    private fun parseCsv(csv : String) : Cook{
        val parts = csv.split(",")
        return Cook(fName = parts[0],
                    lName = parts[1],
                    age = parts[2].toInt(),
                    position = parts[3])
    }

    override fun save(c: Cook, outStream: OutputStream) {
        outStream.use {
            PrintWriter(outStream, true).println(c.toCSV())
        }
    }

    override fun read(inStream: InputStream): Cook {
        inStream.use {
            val sc = Scanner(inStream)
            return parseCsv(sc.nextLine())
        }
    }
}

The CsvCookDao class does the job. There are a couple of things to note about this class that makes it more interesting. The first is the extension function Cook.toCSV(). Kotlin extension functions let us add extra functionality to a class. We could have added a toCSV() method to Cook, but let’s think about the implications of such a design. For one thing, only CsvCookDao is concerned about making a Cook into a line of CSV. If we added toCSV() to Cook, then we are saying that all Cook objects should be able to turn themselves into CSV at any time.

That’s bad because we are violating the separation of concerns principle again. We don’t want Cook to concern itself with persistence. That’s the job of our DAO classes. Thus, our CsvCookDao gets a private method to convert Cooks into CSV. Likewise, we have the parseCsv function which also turns CSV back into Cooks. We could violate the separation of concerns by adding a constructor to the Cook class, but that would be equally as bad as adding a toCSV() method.

Now that we have our DAO classes, we need a way to pass Cooks from the view to the DAO. We can define a service class for this purpose.


/**
 * CookService uses Kotlin's delegation mechanism. CookService will have
 * all of the same methods as CookDao but the implementation will depend
 * on the CookDao that was provided
 */
class CookService(private val cookDao: CookDao) : CookDao by cookDao

The CookService class is powerful but incredibly compact. Instances of this class are using Kotlin’s delegation mechanism where all of the methods of CookService are wrapped by methods found in CookDao. It’s this mechanism that lets our application switch between serialization and CSV so easily. The view will use CookService. The CookService is created by passing an instance of CookDao to the constructor of CookService. Whichever CookDao is used will determine the data format the application is currently using!

Let’s have a look at the final portion of the application to see this in action.

fun saveAndRestore(bob : Cook, cookService : CookService, inStream : InputStream, outStream : OutputStream){
    println("Bob before saving => " + bob)

    cookService.save(bob, outStream)

    var restoredBob = cookService.read(inStream)
    println("Bob after restoring => " + restoredBob)
}

fun createIfNeeded(name : String) : File{
    val f = File(name)
    if(!f.exists()){
        f.createNewFile()
    }
    return f
}

fun main(args : Array<String>){
    val bob = Cook("Bob", "Belcher", "Owner", 45)
    println("Using CSV")
    saveAndRestore(bob,
            CookService(CsvCookDao()), //Application is using CSV
            FileInputStream(createIfNeeded("bob.csv")),
            FileOutputStream(createIfNeeded("bob.csv")))
    println()
    println("Using serialization")
    saveAndRestore(bob,
            CookService(SerializedCookDao()), //Application is using Serialization
            FileInputStream(createIfNeeded("bob.ser")),
            FileOutputStream(createIfNeeded("bob.ser")))
}

The saveAndRestore function does the job of saving and restoring a Cook object. However, it has no idea of where or how a Cook is handled. The saveAndRestore function interacts soley with CookService. The CookService objects are created in the main method. When a CsvCookDao is used in the CookService constructor, the Cooks are saved in CSV format. When SerialiazedCookDao is used instead, the application will use JVM serialization to save and restore a Cook.

It’s easy to imagine how easily this program can be extended later on. Suppose we which to support XML. All we need to do is create a new CookDao class that handles the transformation of a Cook object to and from XML. Later on, we would pass this CookDao class to the constructor of CookService and the rest of the application would continue to work. In this fashion, we can easily continue to add additional file formats as needs change.

Putting it Together

Here is the program in its entirety followed by output.

package ch4.dataacesspattern

import java.io.*
import java.util.*

/**
 * This a model class that is persisted to disk in this program
 */
data class Cook(val fName : String,
                val lName : String,
                val position : String,
                val age : Int) : Serializable

/**
 * This interface describes the kinds of Data Access Operations available
 */
interface CookDao {
    fun save(c : Cook, outStream: OutputStream)

    fun read(inStream: InputStream) : Cook
}

/**
 * Implementation of CookDao that uses JVM serialization
 */
class SerializedCookDao : CookDao{
    override fun save(c: Cook, outStream: OutputStream) {
        val o = ObjectOutputStream(outStream)
        outStream.use {
            o.writeObject(c)
        }
    }

    override fun read(inStream: InputStream) : Cook{
        val i = ObjectInputStream(inStream)
        inStream.use {
            return i.readObject() as Cook
        }
    }
}

/**
 * Implementation of CookDao that converts Cooks to a CSV file
 */
class CsvCookDao : CookDao {

    //Private extension function on Cook for creating
    //CSV files
    private fun Cook.toCSV() : String {
        return "${this.fName},${this.lName},${this.age},${this.position}"
    }

    //Private function to convert a CSV line to a Cook
    private fun parseCsv(csv : String) : Cook{
        val parts = csv.split(",")
        return Cook(fName = parts[0],
                    lName = parts[1],
                    age = parts[2].toInt(),
                    position = parts[3])
    }

    override fun save(c: Cook, outStream: OutputStream) {
        outStream.use {
            PrintWriter(outStream, true).println(c.toCSV())
        }
    }

    override fun read(inStream: InputStream): Cook {
        inStream.use {
            val sc = Scanner(inStream)
            return parseCsv(sc.nextLine())
        }
    }
}

/**
 * CookService uses Kotlin's delegation mechanism. CookService will have
 * all of the same methods as CookDao but the implementation will depend
 * on the CookDao that was provided
 */
class CookService(private val cookDao: CookDao) : CookDao by cookDao

fun saveAndRestore(bob : Cook, cookService : CookService, inStream : InputStream, outStream : OutputStream){
    println("Bob before saving => " + bob)

    cookService.save(bob, outStream)

    var restoredBob = cookService.read(inStream)
    println("Bob after restoring => " + restoredBob)
}

fun createIfNeeded(name : String) : File{
    val f = File(name)
    if(!f.exists()){
        f.createNewFile()
    }
    return f
}

fun main(args : Array<String>){
    val bob = Cook("Bob", "Belcher", "Owner", 45)
    println("Using CSV")
    saveAndRestore(bob,
            CookService(CsvCookDao()), //Application is using CSV
            FileInputStream(createIfNeeded("bob.csv")),
            FileOutputStream(createIfNeeded("bob.csv")))
    println()
    println("Using serialization")
    saveAndRestore(bob,
            CookService(SerializedCookDao()), //Application is using Serialization
            FileInputStream(createIfNeeded("bob.ser")),
            FileOutputStream(createIfNeeded("bob.ser")))
}

Output

Using CSV
Bob before saving => Cook(fName=Bob, lName=Belcher, position=Owner, age=45)
Bob after restoring => Cook(fName=Bob, lName=Belcher, position=Owner, age=45)

Using serialization
Bob before saving => Cook(fName=Bob, lName=Belcher, position=Owner, age=45)
Bob after restoring => Cook(fName=Bob, lName=Belcher, position=Owner, age=45)

Kotlin Abstract Factory Pattern

The Abstract Factory pattern builds on the Factory pattern by abstracting the concrete factory implementation behind an abstract class or an interface. It allows us to loosely couple our factories in situations where we are making a family classes. Rather than having one factory class for each concrete class, we can have specialized factories that build a specific set of classes.

The code remains loosely coupled from both the concrete class itself but also from the factory that creates the class. By using the factory itself as an abstraction point, we can add additional factories later on with their own sets of concrete classes. In the end, not only is client code disconnected from the actual object in use, but it’s also loosely coupled from the creation of the object.

This post shows a demonstration of the Abstract Factory Pattern. We have an interface, CookFactory that abstracts two concrete classes. One of the Factory classes is a BurgerCookFactory that produces Cooks who make burgers. The other Factory is a PizzaCookFactory that produces Cooks who make pizzas. The client code uses the Cook interface.

Let’s begin with the interfaces that serve as abstraction points for both Cooks and Factories.

/**
 * Our factories are going to produce objects of type Cook
 */
interface Cook {
    val name : String
    fun cook()
}

/**
 * BurgerCook is made by the BurgerCookFactory
 */
interface BurgerCook : Cook

/**
 * PizzaCook is made by the PizzaCookFactory
 */
interface PizzaCook : Cook

/**
 * The CookFactory is the base interface for our Factory
 */
interface CookFactory {
    fun getInstance(name : String) : Cook
}

We have four interfaces here. Cook is the base interface of all Cook objects. From Cook, we have two subinterfaces called BurgerCook and PizzaCook. The final interface is the CookFactory, which produces all Cook objects. Of course, we now need some concrete classes to go with these interfaces, so let’s begin with BurgerCookFactory.

/**
 * BurgerCookFactory implements CookFactory and serves as a concrete class to
 * make Cooks that make burgers
 */
class BurgerCookFactory : CookFactory {
    override fun getInstance(name: String) : BurgerCook {
       return when (name){
            "Bob" -> Bob()
            "Tina" -> Tina()
            else -> throw IllegalArgumentException("No class available for $name")
        }
    }

    /**
     * Bob is one concrete class
     */
    private class Bob : BurgerCook {
        override val name: String
            get() = "Bob"

        override fun cook() {
            println("Bob cooked One Fish, Two Fish, Red Fish Hamburger")
        }

    }

    private class Tina : BurgerCook {
        override val name: String
            get() = "Tina"

        override fun cook() {
            println("Tina dropped the burger on the floor while cooking it")
        }

    }
}

The above BurgerCookFactory is responsible for making all BurgerCooks. You will notice that it’s making use of covarient return types by returning BurgerCook rather than Cook. The intent behind BurgerCookFactory is to group all classes that implement BurgerCook into this factory class so that this factory is responsible for this family of classes. Inside of BurgerCookFactory, we have two BurgerCooks, Bob and Tina. We are free to add more BurgerCooks later on and need not worry about breaking existing client code.

After making our BurgerCooks, we decided that we need PizzaCooks also. The existing framework we have makes this fairly straightforward. We only need a PizzaCookFactory.

/**
 * This factory is for PizzaCooks
 */
class PizzaCookFactory : CookFactory {
    override fun getInstance(name: String) : PizzaCook {
        return when (name){
            "Jimmy" -> Jimmy()
            "Jr" -> JimmyJr()
            else -> throw IllegalArgumentException("No class available for $name")
        }
    }

    private class Jimmy : PizzaCook {
        override val name: String
            get() = "Jimmy"

        override fun cook() {
            println("Jimmy is cooking a pizza")
        }

    }

    private class JimmyJr : PizzaCook {

        override val name: String
            get() = "Jimmy Junior"

        override fun cook() {
            println("Jimmy Junior started dancing rather than cooking a pizza")
        }
    }
}

The PizzaCookFactory is another Factory class that implements CookFactory. It makes Cooks that implement the PizzaCook interface, which in turn extends the Cook interface. At this point, PizzaCookFactory is usable anywhere Cooks and CookFactory are needed. As our program grows and changes, we can continue to add more factories and cooks when needed.

The Client code for using the factories is really simple. Here is a simple function that accepts a factory and returns a cook.

fun makeCook(factory: CookFactory, name : String) : Cook {
    return factory.getInstance(name)
}

Of course, this is a gross simplification but demonstrates the point. The makeCook function does not care if the factory is BurgerCookFactory or PizzaCookFactory. It only cares that is has a factory of some sort and returns a cook of some sort. Any CookFactory will do and so will all Cooks.

Putting it Together

Here is a complete working program that demonstrates the Abstract Factory Pattern followed by the output.

package ch4.abstractfactory

/**
 * Our factories are going to produce objects of type Cook
 */
interface Cook {
    val name : String
    fun cook()
}

/**
 * BurgerCook is made by the BurgerCookFactory
 */
interface BurgerCook : Cook

/**
 * PizzaCook is made by the PizzaCookFactory
 */
interface PizzaCook : Cook

/**
 * The CookFactory is the base interface for our Factory
 */
interface CookFactory {
    fun getInstance(name : String) : Cook
}

/**
 * BurgerCookFactory implements CookFactory and serves as a concrete class to
 * make Cooks that make burgers
 */
class BurgerCookFactory : CookFactory {
    override fun getInstance(name: String) : BurgerCook {
       return when (name){
            "Bob" -> Bob()
            "Tina" -> Tina()
            else -> throw IllegalArgumentException("No class available for $name")
        }
    }

    /**
     * Bob is one concrete class
     */
    private class Bob : BurgerCook {
        override val name: String
            get() = "Bob"

        override fun cook() {
            println("Bob cooked One Fish, Two Fish, Red Fish Hamburger")
        }

    }

    private class Tina : BurgerCook {
        override val name: String
            get() = "Tina"

        override fun cook() {
            println("Tina dropped the burger on the floor while cooking it")
        }

    }
}

/**
 * This factory is for PizzaCooks
 */
class PizzaCookFactory : CookFactory {
    override fun getInstance(name: String) : PizzaCook {
        return when (name){
            "Jimmy" -> Jimmy()
            "Jr" -> JimmyJr()
            else -> throw IllegalArgumentException("No class available for $name")
        }
    }

    private class Jimmy : PizzaCook {
        override val name: String
            get() = "Jimmy"

        override fun cook() {
            println("Jimmy is cooking a pizza")
        }

    }

    private class JimmyJr : PizzaCook {

        override val name: String
            get() = "Jimmy Junior"

        override fun cook() {
            println("Jimmy Junior started dancing rather than cooking a pizza")
        }
    }
}

fun makeCook(factory: CookFactory, name : String) : Cook {
    return factory.getInstance(name)
}

fun main(args : Array<String>){
    val burgerFactory = BurgerCookFactory()
    val pizzaFactory = PizzaCookFactory()

    var cook = makeCook(burgerFactory, "Bob")
    cook.cook()

    println()
    cook = makeCook(burgerFactory, "Tina")
    cook.cook()

    println()
    cook = makeCook(pizzaFactory, "Jimmy")
    cook.cook()

    println()
    cook = makeCook(pizzaFactory, "Jr")
    cook.cook()
}

Output

Bob cooked One Fish, Two Fish, Red Fish Hamburger

Tina dropped the burger on the floor while cooking it

Jimmy is cooking a pizza

Jimmy Junior started dancing rather than cooking a pizza

Kotlin Factory Pattern

The Factory Pattern is an OOP design pattern where a client requests one object to create an instance of another object for it. The actual object is normally hidden behind an interface to allow for loose coupling, but the factory knows how to produce the correct object. Let’s consider for example a client that needs a product.

The Product is an interface representing a product. The client doesn’t know what sort of product it is, nor should it care. When a product is needed the client can call a method on a ProductFactory object and tell it what sort of product to product. Internally, the ProductFactory parses the arguments and creates an instance of a usually private class the implements the Product interface. The new object is returned to the caller.

In our example, we will have a Cook that is produced by a CookFactory. The client will specify the kind of cook it wants and the CookFactory will produce the correct instance of Cook. The client will never actually know what sort of Cook it is getting, but that should not be of any concern to the client anyway.

/**
 * A Cook interface that is implemented by private classes in the factory
 */
interface Cook {
    val name : String

    fun cook()
}

We can’t directly create a cook because it’s an interface. So to get an instance of a Cook, we need a class that implements this interface. Our CookFactory singleton provides the necessary classes.

/**
 * A singleton class that produces cooks
 */
object CookFactory {
    enum class CookType {BOB, JIMMY}

    /**
     * Called by clients to get a Cook based on the CookType
     */
    fun getCookInstance(type : CookType) : Cook{
        return when (type) {
            CookType.BOB -> Bob()
            CookType.JIMMY -> Jimmy()
        }
    }

    /**
     * Bob is a Cook that is private to the Factory. Only the Factory can
     * create Bob
     */
    private class Bob : Cook {
        override val name: String
            get() = "Bob"

        override fun cook() {
            println("Bob is cooking the Longest Chard Burger\n")
        }
    }

    /**
     * Jimmy is a Cook that is private to the Factory. Only the Factory can
     * create Jimmy
     */
    private class Jimmy : Cook {
        override val name: String
            get() = "Jimmy"

        override fun cook() {
            println("Jimmy is cooking a pizza\n")
        }

    }
    
    //Note that we can add other cooks here also!
}

The CookFactory only has one method: getCookInstance(). It accepts a value from the CookType enum class and returns an instance of Cook based on the input. Right now the cook is either Bob or Jimmy. It’s important to keep in mind that this is providing an abstraction point.

For example, let’s suppose we want to add more cooks as we maintain the program. We can easily tuck away new cooks inside of the CookFactory object or add additional arguments to the CookType enum or the getCookInstance() method. The clients of CookFactory still work as needed since they are working with the Cook interface rather than concrete Cook classes. Since the Factory Pattern is forcing us to program to an interface rather than a type, we are not only loosely coupled, but our code is highly flexible and maintainable.

Putting it Together

package ch4.factory

/**
 * A Cook interface that is implemented by private classes in the factory
 */
interface Cook {
    val name : String

    fun cook()
}

/**
 * A singleton class that produces cooks
 */
object CookFactory {
    enum class CookType {BOB, JIMMY}

    /**
     * Called by clients to get a Cook based on the CookType
     */
    fun getCookInstance(type : CookType) : Cook{
        return when (type) {
            CookType.BOB -> Bob()
            CookType.JIMMY -> Jimmy()
        }
    }

    /**
     * Bob is a Cook that is private to the Factory. Only the Factory can
     * create Bob
     */
    private class Bob : Cook {
        override val name: String
            get() = "Bob"

        override fun cook() {
            println("Bob is cooking the Longest Chard Burger\n")
        }
    }

    /**
     * Jimmy is a Cook that is private to the Factory. Only the Factory can
     * create Jimmy
     */
    private class Jimmy : Cook {
        override val name: String
            get() = "Jimmy"

        override fun cook() {
            println("Jimmy is cooking a pizza\n")
        }

    }

    //Note that we can add other cooks here also!
}

fun main(args : Array<String>){
    val bob = CookFactory.getCookInstance(CookFactory.CookType.BOB)
    val jimmy = CookFactory.getCookInstance(CookFactory.CookType.JIMMY)

    println("Testing Bob")
    bob.cook()

    println("Testing Jimmy")
    jimmy.cook()
}

Output

Testing Bob
Bob is cooking the Longest Chard Burger

Testing Jimmy
Jimmy is cooking a pizza

Kotlin Singletons

Singleton objects is a design pattern that allows for one and only one instance of a class to exist at a time. Singletons are useful when declaring classes that manage a single shared resource such as a logging file or a network connection. We can use a single object to work with such a resource as opposed to have a bunch of objects in memory that needs synchronized access instead.

Kotlin uses object expressions to create singletons. When we declare an object expression at the top level of a Kotlin module, it becomes a singleton. Here is an example.

object Bob {
    val fName = "Bob"
    val lName = "Belcher"

    fun cook(burgerName : String){
        println("Cooking $burgerName")
    }
}

Many readers will notice that the definition of this Bob object looks almost exactly like a class. That is indeed the case as a matter of fact, only we replace the class keyword with object instead.

We can use a singleton by using the object name followed by a method or property.

fun main(args : Array<String>){
    Bob.cook("Pear Goes the Neighborhood")
}

As a matter of fact, Bob is a global object and is used like any other variable. Developers who are familiar with Java may think of these as static methods, but that is not the case. Static methods are associated with a class. In this case, Bob is actually an instance of a Bob class. His methods belong to an instance, not a class itself.

Since Bob is a singleton, he has some rules. We are free to assign Bob to other variables, but we can’t make more Bob’s (he wouldn’t be a singleton otherwise). Bob also has to be initialized right away with a no-argument constructor (after all, he is created by the runtime). Besides those rules, we use singletons just like any other objects. For example, we could have Bob extend classes or even implement interfaces.

By making Bob a singleton, we are ensuring that only one instance of Bob exists in memory. We are free to use singleton objects just like any other Kotlin object, but we are not free to make new instances of a singleton. Singletons are useful for controlling access to shared resources such as thread pools, logging, caching, or other shared resources. Kotlin makes creating singletons easy by using object expressions.

Putting it Together

Here is a demonstration of singletons followed by program output.

object Bob {
    val fName = "Bob"
    val lName = "Belcher"

    fun cook(burgerName : String){
        println("Cooking $burgerName")
    }
}

fun main(args : Array<String>){
    Bob.cook("Pear Goes the Neighborhood")
}

Output

Cooking Pear Goes the Neighborhood

Observer Pattern in Kotlin

JDK provides java.util.Observable and java.util.Observer as an abstract class / interface combination that allows an easy mechanism with which to implement the observer pattern. The framework allows one class to monitor changes in another class and react as needed to the changes. By using the class and interface provided in JDK, we can loosely couple one class to another while still maintaining the observer pattern.

We are going to use three classes in this example: Burger, Bob, and Tina. Bob is a cook who cooks Burgers. Tina is a server who serves the Burgers, and Burger is the object that is passed between Bob and Tina. Bob will notify Tina when a burger is ready, but he doesn’t actually know anything about Tina. All Bob knows (or cares about) is that Tina is an observer.

Let’s begin with both Burger and Bob (since Burger is one line of code).

class Burger(val name: String)

class Bob : Observable() {

    val name = "Bob"

    fun cookBurger(name : String){
        var burger = Burger(name)

        //Call setChanges() prior to calling notifyObservers()
        setChanged() //Inherited from Observable()
        notifyObservers(burger) //Inherited from Observable()
    }
}

We see here that Bob extends Observable. Observable is an abstract class found in the java.util package. In the cookBurger method, Bob creates an new instance of Burger and then calls setChanged(), a method defined in Observable. After calling setChanged(), Bob calls notifyObservers(), which also comes from Observable. All registered Observer objects are notified and since Bob passes the new Burger object as an argument to notifyObservers, they recieve the Burger Bob created.

The Tina class is the receiving end of this pattern. Tina doesn’t know about Bob or any other Observable. She only acts as a receiver for all Observables.

class Tina : Observer{

    val name = "Tina"

    override fun update(o: Observable?, arg: Any?) {
        when (o){
            is Bob -> {
                if (arg is Burger){
                    println("$name is serving the ${arg.name} cooked by ${o.name}")
                }
            }
            else -> println(o?.javaClass.toString())
        }
    }
}

Since Observer is an interface, Tina is forced to implement the update method. The update method has two parameters. The first one is the Observable that is being updated. The other argument is any additional information the Observable passed when calling notifyObservers().

Kotlin distinguishes between nullable and non-null types but Java does not. Anytime we choose to implement Java interfaces or extend abstract classes found in Java, we have to choose if we want null safety or not. There is no way for the Kotlin compiler to know.

In Tina’s case, we have chosen to declare both o and arg as nullable types by adding the question mark (?) after their types. By doing so, we are choosing to accept the null safety offered by the kotlin compiler. We are free to choose otherwise and disregard the null safety, provided we are reasonably sure the types are not going to be null. For example, when I use the Spring framework in Kotlin, I rarely use the null safety because I am usually correct that I am not going to get null from a Spring method.

In this case, I felt it’s better to use the null safety. Observable and Observer are older classes, and we don’t know for sure what may get passed in as parameters to update. Inside of the implementation of update, we have to cast our parameters to the types we need. We can use is operator combined with the when function (see casting). We check if o is Bob and if arg is Burger. When they are, we print out that a Burger is getting served.

There is one final piece to this example. Although Bob and Tina can now work together thanks to Observable and Observer, they need to know about each other. Since Bob extends Observable, he has an addObserver() method that let’s us pass in an instance of Observer.

fun main(args : Array<String>){
    val bob = Bob()
    //Provide Bob and instance of Tina
    bob.addObserver(Tina())

    bob.cookBurger("It takes Bun to Know Bun Burger")
}

Going forward, whenever Bob calls cookBurger, Tina will get notified and serve it.

Putting it Together

Here is the complete program with its output.

package ch4.observer

import java.util.*

class Burger(val name: String)

class Bob : Observable() {

    val name = "Bob"

    fun cookBurger(name : String){
        var burger = Burger(name)

        //Call setChanges() prior to calling notifyObservers()
        setChanged() //Inherited from Observable()
        notifyObservers(burger) //Inherited from Observable()
    }
}

class Tina : Observer{

    val name = "Tina"

    override fun update(o: Observable?, arg: Any?) {
        when (o){
            is Bob -> {
                if (arg is Burger){
                    println("$name is serving the ${arg.name} cooked by ${o.name}")
                }
            }
            else -> println(o?.javaClass.toString())
        }
    }
}

fun main(args : Array<String>){
    val bob = Bob()
    bob.addObserver(Tina())

    bob.cookBurger("It takes Bun to Know Bun Burger")
}

Output

Tina is serving the It takes Bun to Know Bun Burger burger cooked by Bob

Kotlin Interfaces

Interfaces provide an abstract point between two entities. They work by defining a set of behaviors that implementing classes are expected to implement. Rather than using a concrete class, a client of a class can use the interface as an abstraction point providing loose coupling between objects. Loose coupling promotes code reuse because a client is free to use any object that implements the interface.

Kotlin interfaces look similar to classes, but use the interface keyword rather than the class keyword.

/**
 * This is a basic interface called Server. It defines a single abstract method called serveOrder.
 * All classes that implement Server have to define serveOrder.
 */
interface Server {
    fun serveOrder()
}

Our server interface defines a single method, serverOrder(). Note that we cannot create an instance of an interface. So it’s not legal to write code such as this:

val server = Server() //ILLEGAL!

We use Server by writing a class that implements Server.

/**
 * Tina is a class that implements Server. She can be used anywhere a Server is needed.
 */
class Tina : Server {
    /**
     * Since Server doesn't provide a default implementation of serverOrder, it's up to Tina
     * to provide a method body for this function.
     */
    override fun serveOrder() {
        println("Tina is serving an order")
    }

}

Since our Server class only defines abstract methods, the Tina class is expected to override the serverOrder() method. We can then use Tina in any variable that expects a Server.

val server : Server = Tina() //OK because Tina is a Server!

fun orderUp(s : Server) = s.serveOrder()

orderUp(Tina()) //OK because once again, Tina is a Server

Advance Interfaces

Kotlin allows interfaces to have properties and default methods. Here is a Cook interface that we will use an example.

/**
 * This is a cook interface. Kotlin allows us to define properties in our interfaces but unlike abstract
 * class, interfaces may not hold state.
 */
interface Cook {
    val name : String

    /**
     * Interfaces methods can have default implementations
     */
    fun sayHello() = "$name says Hello"

    fun cook()

    /**
     * Notice how this method takes a server object. That means any class that implements Server
     * may be used for this method. In other words, Cook is coupled to Servers but not to any
     * class that implements servers.
     */
    fun orderUp(s : Server) = s.serveOrder()
}

The Cook interface has a name property. However, it is not allowed to assign a value to name. Interfaces can never hold state (use abstract classes instead). Since the Cook interface has such a property, all classes that implement Cook must override the name property.

Cook also has two other methods that have default implementations. The sayHello() method and the orderUp(Server) method. Classes that implement Cook do not need to provide an implementation of these methods. Notice that cook() is still abstract and must be overrode. Unlike classes, methods on an interface are open by default and classes are free to override them as needed.

Bob is a Cook and therefore implements the Cook interface.

/**
 * Bob implements the Cook interface. As such, Bob may be used anywhere we need a cook.
 */
class Bob : Cook {

    /**
     * Cook has a name property. Since interfaces can't hold state, Bob has to override the name property
     */
    override val name: String = "Bob"

    /**
     * It's up to Bob to override the cook() method also.
     */
    override fun cook() {
        println("$name is cooking a burger")
    }

    /**
     * Bob is also free to implement any other method in Cook
     */
    override fun sayHello(): String = "$name speaking here. Hello..."
}

Since Bob impelments Cook, he has to override the name property and the cook() method. Bob has chosen to override sayHello(), but he didn’t have to. Has Bob ignored the sayHello() method, he would have simply inherited the behavior defined in Cook.

Using Interfaces

Interfaces are used like any other value in Kotlin. Here is a function that uses the Cook and Server interface.

/**
 * This demoCook function is loosely coupled to Cook and Server. As such, we are free
 * to use any implementation of Cook or Server
 */
fun demoCook(c : Cook, s : Server, name: String){
    println("Demonstrating Cook with $name")
    with (c){
        println(sayHello())
        cook()
        orderUp(s)
    }
    println()
}

The demoCook function is free to use any implementation of Cook and Server. We may only have Bob and Tina right now but should we decide to use a different class implementing Cook and Server, we are still free to use this function with this class. That is the major advantage of using interfaces. Code that is written to use an interface rather than a concrete class remains highly flexible and maintainable.

Putting it Together

The following is an example Kotlin program that uses interfaces followed by its output.

package ch5.interfaces

/**
 * This is a basic interface called Server. It defines a single abstract method called serveOrder.
 * All classes that implement Server have to define serveOrder.
 */
interface Server {
    fun serveOrder()
}

/**
 * This is a cook interface. Kotlin allows us to define properties in our interfaces but unlike abstract
 * class, interfaces may not hold state.
 */
interface Cook {
    val name : String

    /**
     * Interfaces methods can have default implementations
     */
    fun sayHello() = "$name says Hello"

    fun cook()

    /**
     * Notice how this method takes a server object. That means any class that implements Server
     * may be used for this method. In other words, Cook is coupled to Servers but not to any
     * class that implements servers.
     */
    fun orderUp(s : Server) = s.serveOrder()
}

/**
 * Tina is a class that implements Server. She can be used anywhere a Server is needed.
 */
class Tina : Server {
    /**
     * Since Server doesn't provide a default implementation of serverOrder, it's up to Tina
     * to provide a method body for this function.
     */
    override fun serveOrder() {
        println("Tina is serving an order")
    }

}

/**
 * Bob implements the Cook interface. As such, Bob may be used anywhere we need a cook.
 */
class Bob : Cook {

    /**
     * Cook has a name property. Since interfaces can't hold state, Bob has to override the name property
     */
    override val name: String = "Bob"

    /**
     * It's up to Bob to override the cook() method also.
     */
    override fun cook() {
        println("$name is cooking a burger")
    }

    /**
     * Bob is also free to implement any other method in Cook
     */
    override fun sayHello(): String = "$name speaking here. Hello..."
}

/**
 * Jimmy is also a cook. Notice how he is only expected to implement name and cook()
 */
class Jimmy : Cook {
    override fun cook() {
        println("$name is cooking a pizza")
    }

    override val name: String = "Jimmy"
}

/**
 * This demoCook function is loosely coupled to Cook and Server. As such, we are free
 * to use any implementation of Cook or Server
 */
fun demoCook(c : Cook, s : Server, name: String){
    println("Demonstrating Cook with $name")
    with (c){
        println(sayHello())
        cook()
        orderUp(s)
    }
    println()
}

fun main(args : Array<String>){
    val bob = Bob()
    val jimmy = Jimmy()

    val tina = Tina()

    /**
     * We can implement interfaces with object expressions also
     */
    val jimmyJr = object: Server{
        override fun serveOrder() {
            println("Jimmy Jr is serving an order")
        }
    }

    demoCook(bob, tina, "Bob")
    demoCook(jimmy, jimmyJr, "Jimmy")
}

Output

Demonstrating Cook with Bob
Bob speaking here. Hello...
Bob is cooking a burger
Tina is serving an order

Demonstrating Cook with Jimmy
Jimmy says Hello
Jimmy is cooking a pizza
Jimmy Jr is serving an order

Kotlin Enum Classes

Enum classes are used to restrict choices to a set of predetermined values. For example, you may wish to define a set of color constants such as Red, Green, and Blue. The color constants may later be used as an argument to a function or used in a when function. The compiler checks to make sure that only the allowed values are used when required. The type safety offered by enum classes can really valuable when reducing bugs and since the constants are named, the readability of the code is improved.

Let’s start with a basic definition of enum classes in Kotlin. We are going to begin with a Toppings enum class that offers Ketchup, Lettuce, Mayonnaise, and Tomatoes.

enum class Toppings {
    KETCHUP,
    MAYONNAISE,
    LETTUCE,
    TOMATO
}

We can reference the values in toppings like so

val ketchup = Toppings.KETCHUP
val mayo = Toppings.MAYONNAISE
val lettuce = Toppings.LETTUCE
val tomato = Toppings.TOMATO

or we can even use it as an argument to a function

fun printToppings(t : Toppings){
    if (t == Toppings.KETCHUP){
        print("Ketchup)
    } else {
        print("Not ketchup")
    }
}

Enum classes also work great with the when function

val t = Toppings.LETTUCE

val topping = 
    when (t) {
       Toppings.KETCHUP -> "Ketchup"
       Toppings.MAYONNAISE -> "Mayonnaise"
       Toppings.LETTUCE -> "Lettuce"
       Toppings.TOMATO -> "Tomato"
    }
print(topping)

Enum classes also have a name and ordinal property

val topping = Toppings.MAYONNAISE
println(topping.name) //Prints MAYONNAISE
println(topping.ordinal) //Prints 1

Advanced Enum Classes

Since enum classes are classes, they are free to have properties and methods like any other classes. Here is an example of an enum class with properties.

enum class Burgers(val burgerName : String){
    CAPTAIN_JACK("Captain Pepper Jack Marrow Burger"),
    MY_FARRO_LADY("My Farro Lady Burger"),
    ENDIVE("Endive Had the Time of My Life Burger")
}

Since each constant in the enum class is an instance of the enum class, we need to initialize all properties like we would any other class. We reference the enum’s property just like any other class property

val b = Burgers.CAPTAIN_JACK
println(b.burgerName) //prints Captain Pepper Jack Marrow Burger

We can also define enum classes with methods, including abstract methods. This example shows an enum class where each constant in the enum class overrides the abstract method found in the enum.

enum class Family{

    LOUISE {
        override fun sayCatchPhrase(): String =
                "I can smell fear on you"
    },

    GENE {
        override fun sayCatchPhrase(): String =
                "I can't tell you my full name! You know Mom won't tell me my middle name!"
    },

    TINA {
        override fun sayCatchPhrase(): String =
                "Here's a bunch of numbers that may look random, but they're my phone number"
    },

    LINDA {
        override fun sayCatchPhrase(): String =
                "Teddy will eat whatever you put in front of him. Remember when he ate that receipt?"
    },

    BOB {
        override fun sayCatchPhrase(): String =
                "So that's what a prostate exam is"
    };

    abstract fun sayCatchPhrase() : String
}

We call the method like we would on any other class.

val f = Family.BOB
print(f.sayCatchPhrase()) //Prints So that's what a prostate exam is

Putting it Together

Here is an example program that shows off all three of the enum examples discussed.

package ch4.enumclasses

enum class Toppings {
    KETCHUP,
    MAYONNAISE,
    LETTUCE,
    TOMATO
}

enum class Burgers(val burgerName : String){
    CAPTAIN_JACK("Captain Pepper Jack Marrow Burger"),
    MY_FARRO_LADY("My Farro Lady Burger"),
    ENDIVE("Endive Had the Time of My Life Burger")
}

enum class Family{

    LOUISE {
        override fun sayCatchPhrase(): String =
                "I can smell fear on you"
    },

    GENE {
        override fun sayCatchPhrase(): String =
                "I can't tell you my full name! You know Mom won't tell me my middle name!"
    },

    TINA {
        override fun sayCatchPhrase(): String =
                "Here's a bunch of numbers that my look random, but they're my phone number"
    },

    LINDA {
        override fun sayCatchPhrase(): String =
                "Teddy will eat whatever you put in front of him. Remember when he ate that receipt?"
    },

    BOB {
        override fun sayCatchPhrase(): String =
                "So that's what a prostate exam is"
    };

    abstract fun sayCatchPhrase() : String
}

fun familyDemo(f : Family){
    println("${f.name} says ${f.sayCatchPhrase()}")
}

fun main(args : Array<String>){
    //Enum classes have a name and ordinal property
    println("Printing all values found in Toppings")
    Toppings.values().forEach { println("${it.name}: ${it.ordinal}") }.also { print("\n") }

    //We can also add our own properties like we did in Burgers
    println("Printing all values found in Burgers with burgerName")
    Burgers.values().forEach { println("${it.name}: ${it.ordinal} => ${it.burgerName}") }.also { print("\n")}

    //And we can even call methods on the enum class
    println("Printing all values found in Family with catchPhrase()")
    Family.values().forEach { println("${it.name}: ${it.ordinal} => ${it.sayCatchPhrase()}") }
    println()

    var burger = "Burger with "
    val topping = Toppings.LETTUCE

    //Enums work great with the when function
    burger += when (topping){
        Toppings.KETCHUP -> "ketchup"
        Toppings.MAYONNAISE -> "mayonnaise"
        Toppings.LETTUCE -> "lettuce"
        Toppings.TOMATO -> "tomato"
    }

    println("Burger is => $burger \n")

    val family = Family.GENE
    familyDemo(family)
}

We get the following output when the program is run.

Printing all values found in Toppings
KETCHUP: 0
MAYONNAISE: 1
LETTUCE: 2
TOMATO: 3

Printing all values found in Burgers with burgerName
CAPTAIN_JACK: 0 => Captain Pepper Jack Marrow Burger
MY_FARRO_LADY: 1 => My Farro Lady Burger
ENDIVE: 2 => Endive Had the Time of My Life Burger

Printing all values found in Family with catchPhrase()
LOUISE: 0 => I can smell fear on you
GENE: 1 => I can't tell you my full name! You know Mom won't tell me my middle name!
TINA: 2 => Here's a bunch of numbers that my look random, but they're my phone number
LINDA: 3 => Teddy will eat whatever you put in front of him. Remember when he ate that receipt?
BOB: 4 => So that's what a prostate exam is

References

https://kotlinlang.org/docs/reference/enum-classes.html

Kotlin Companion Objects

Kotlin has a companion object feature that lets us associate a single object with a class. Companion objects can serve a number of purposes. For example, we may wish to associate methods such as factory methods with a companion object rather than an instance of a class. Likewise, we can use companion object to store constants or count the number of times an instance of a class was created.

Here is an example Burger class that uses a companion object.

class Burger(val name : String) {
    
    //Create and define a companion object.
    companion object SpecialBurgers {
        //Here are some constants
        const val TOP_BUN_BURGER = "Top Bun Burger"
        const val NATIONAL_PASS_THYME_BURGER = "National Pass Thyme Burger"
        const val KALE_MARY_BURGER = "Kale Mary Burger"
        const val SNIPWRECKED_BURGER = "Snipwrecked Burger"

        //Count the number of burgers that were created
        private var _instances = 0
        
        val burgers : Int
            get() = _instances

        fun burgerList() =
                listOf(TOP_BUN_BURGER,
                        NATIONAL_PASS_THYME_BURGER,
                        KALE_MARY_BURGER,
                        SNIPWRECKED_BURGER)
    }

    init {
        check(name in burgerList(), { "$name not in Burger List"})
        
        //Increment the number of burgers created
        _instances++
    }
}

The Burger class has a companion object called SpecialBurgers. Readers will notice that creating a companion object is almost the same as writing a class. The difference is that the companion object is instantiated with the JVM loads the Burger class into memory. Companion objects can have init blocks, but they are not allowed to have constructors. Finally, we are not allowed to create instances of companion objects.

The SpecialBurgers object has four constants, a variable called _instances, and some methods. The init block in Burger increments _instances. It also calls burgerList() found in the companion object to check if the name variable is found in burgerList(). If the name isn’t in burgerList(), the program will throw an exception.

We can use the companion object later on by using the class name followed by the dot (.) syntax. Here is the later half of the program that uses the companion object.

fun main(args : Array<String>){
    val badBurger = "Barley Davidson Burger"

    try {
        //This will throw an exception because of the
        //check in Burger's init block
        val barleyBurger = Burger(badBurger)
    } catch (e : Exception){
        println("$badBurger is not in ${Burger.burgerList()}")
    }

    //We can reference the KALE_MARY_BURGER constant found in 
    //Burger's companion object
    val firstBurger = Burger.KALE_MARY_BURGER
    val kaleBurger = Burger(firstBurger)
    
    //This next line uses the burgers property found in SpecialBurgers to
    //print how many burgers we have created
    println("Created ${kaleBurger.name} and we have served ${Burger.burgers} burgers")

    //We can also reference our burger constants directly
    val snipsBurger = Burger(Burger.SNIPWRECKED_BURGER)
    println("Created ${snipsBurger.name} and we have served ${Burger.burgers} burgers")

    //Finally, the SpecialBurgers is an actual object, so we
    //can store it in a variable when needed
    val burgerCompanion = Burger.SpecialBurgers
    
    val topBunBurger = Burger(burgerCompanion.TOP_BUN_BURGER)
    println("Created ${topBunBurger.name} and we have served ${Burger.burgers} burgers")
}

We use the SpecialBurgers object in a few different ways in main. First, we try and make a barleyBurger. Creating a barleyBurger throws an exception because it’s burger name, “Barley Davidson Burger” is fails the name check found in Burger’s init block.

The next burger we try making is the kaleBurger. First we show how to store a value found in Burger.KALE_MARY_BURGER in a variable. As you can see, it acts just like any other variable. The next line creates an instance of Burger by passing firstBurger to the constructor of Burger. Creating the kaleBurger succeeds and the init block in Burger increments the _instances variable by one. The next line prints out the name of the burger and how many burgers we have served. Notice that it’s Burger.burgers because we are using the burger property found on SpecialBurgers.

The snipsBurger is created by passing Burger.SNIPWRECKED_BURGER direclty into the Burger’s constructor. Once against _instances is incremented and we now have served two specialty burgers. The final burger is created by first assigning SpecialBurgers to a variable first. Since SpecialBurgers is an object, it can be assigned to other references when needed. We create our topBunBurger by using burgerCompanion.TOP_BUN_BURGER.

Putting it Together

Here is the entire program, followed by the output.

package ch4.companionobjects

class Burger(val name : String) {
    companion object SpecialBurgers {
        //Heere are some constants
        const val TOP_BUN_BURGER = "Top Bun Burger"
        const val NATIONAL_PASS_THYME_BURGER = "National Pass Thyme Burger"
        const val KALE_MARY_BURGER = "Kale Mary Burger"
        const val SNIPWRECKED_BURGER = "Snipwrecked Burger"

        //Count the number of burgers that were created
        private var _instances = 0

        val burgers : Int
            get() = _instances

        fun burgerList() =
                listOf(TOP_BUN_BURGER,
                        NATIONAL_PASS_THYME_BURGER,
                        KALE_MARY_BURGER,
                        SNIPWRECKED_BURGER)
    }

    init {
        check(name in burgerList(), { "$name not in Burger List"})

        //Increment the number of burgers created
        _instances++
    }
}

fun main(args : Array<String>){
    val badBurger = "Barley Davidson Burger"

    try {
        //This will throw an exception because of the
        //check in Burger's init block
        val barleyBurger = Burger(badBurger)
    } catch (e : Exception){
        println("$badBurger is not in ${Burger.burgerList()}")
    }

    //We can reference the KALE_MARY_BURGER constant found in
    //Burger's companion object
    val firstBurger = Burger.KALE_MARY_BURGER
    val kaleBurger = Burger(firstBurger)

    //This next line uses the burgers property found in SpecialBurgers to
    //print how many burgers we have created
    println("Created ${kaleBurger.name} and we have served ${Burger.burgers} burgers")

    //We can also reference our burger constants directly
    val snipsBurger = Burger(Burger.SNIPWRECKED_BURGER)
    println("Created ${snipsBurger.name} and we have served ${Burger.burgers} burgers")

    //Finally, the SpecialBurgers is an actual object, so we
    //can store it in a variable when needed
    val burgerCompanion = Burger.SpecialBurgers

    val topBunBurger = Burger(burgerCompanion.TOP_BUN_BURGER)
    println("Created ${topBunBurger.name} and we have served ${Burger.burgers} burgers")
}

This is the output when run.

Barley Davidson Burger is not in [Top Bun Burger, National Pass Thyme Burger, Kale Mary Burger, Snipwrecked Burger]
Created Kale Mary Burger and we have served 1 burgers
Created Snipwrecked Burger and we have served 2 burgers
Created Top Bun Burger and we have served 3 burgers

References

https://kotlinlang.org/docs/reference/properties.html

Kotlin Nested Classes

Nested classes are classes that exist inside of another class. They serve a variety of purposes. For example, we may use a nested class to associate an object with its outer class. Some classes are used to implement another class but only have meaning inside of the class they are used in. Think about a Node class that is used inside of a Linked List implementation. A Node on its own doesn’t have a lot of value outside of a Linked List. Finally, we may use classes that only have a single use and are no longer needed. Event handling code is a common use case for such classes.

Kotlin supports nested classes, inner classes, and anonymous inner classes. Let’s examine each in detail.

Nested Classes

Nested classes are most often used to associate two related classes together where one class would have very little meaning or no meaning without its outer class. Builder objects are a common use case for such classes. Let’s take a look at an example.

/**
 * The Restaurant class is using the Builder pattern. It has a private
 * constructor. Inside of the Restaurant class is a RestaurantBuilder class. The
 * RestaurantBuilder has access to Restaurant's private constructor, but not it's properties
 */
class Restaurant private constructor(val name: String, val address: String) {

    class RestaurantBuilder {
        var name: String = ""
        var address: String = ""

        fun addName(name: String): RestaurantBuilder {
            this.name = name
            return this
        }

        fun addAddress(address: String): RestaurantBuilder {
            this.address = address
            return this
        }

        fun build(): Restaurant = Restaurant(name, address)
    }

    override fun toString(): String = "$name, $address"
}

Creating a nested class in Kotlin just involves writing another class inside of its outer class. The nested class has access all of the outer class’s behavior, but it does not hold a reference to the outer class and as such, it may not access any of the outer class’ properties. In the above code snippet, our RestaurantBuilder class has a build() method that can use Restaurant’s private constructor.

This is how we would use the RestaurantBuilder class to create an instance of Restaurant.

val builder = Restaurant.RestaurantBuilder()
val bobsBurgers = builder
                    .addAddress("104 N Main")
                    .addName("Bob's Burgers")
                    .build()
println(bobsBurgers)

Inner Classes

Inner classes are similar to nested classes but they hold a reference to the outer class instance. This allows the inner class object to use both state and behavior from that belongs to the outer object. For this reason, an inner class object can only be created within the outer class. Attempting to create an inner class object outside of the outer class results in a compiler error.

Linked lists are a good use case for inner classes. Many Linked list implementations use a helper Node class to help manage the implementation of the list. The Node really has no use outside of the linked list, so it doesn’t make sense to make it a standalone class. Also, since the Node may need to access the Linked Lists’s private data, it’s helpful to make it an inner class.

This DeliveryRoute class is a Linked List like object that uses an inner class. All of the Stop class instances refer to the next stop on the route.

/**
 * DeliveryRoute has a Stop inner class. Since Stop is an inner class, Stop has
 * access to both the properties and methods of DeliveryRoute
 */
class DeliveryRoute {
    var currentStop: Stop?

    init {
        currentStop = Stop("Bobs",
                Stop("Jimmys",
                        Stop("Teddys",
                                Stop())))
    }

    inner class Stop(val name: String? = null, val next: Stop? = null) {
        fun advance(): Stop? {
            //CurrentStop is a property in DeliveryRoute
            currentStop = currentStop?.next
            return currentStop
        }

        override fun toString(): String {
            return this.name ?: "End of Route"
        }
    }
}

Kotlin inner classes are created by using the inner keyword in front of the class keyword inside the body of an outer class. Our stop class works like both a linked list node and an iterator. It holds a reference to the next stop and the advance method updates DeliveryRoute’s current stop property. When we use it later on, we can use the stop object to advance through the delivery router.

val route = DeliveryRoute()
var stop : DeliveryRoute.Stop? = route.currentStop

while(stop != null){
    println(route.currentStop)
    stop = stop.advance()
}

Anonymous Inner Classes

Anonymous inner classes are classes that have no name (and are therefore anonymous) and are created on the fly. The most common use case is GUI event handling code. This is because the code for each control on GUI is specific to each control and it, therefore, makes no sense to have named classes for each event object.

Kotlin uses the object expressions to create anonymous inner classes.

fun addAction(e: Runnable) {
    println(e.toString())
}

addAction(object: Runnable{
        override fun run() {}

        override fun toString(): String = "Object expression toString()"

})

The object expression can be used with both abstract classes and interfaces. Lambda expressions are generally preferable when dealing with single method interfaces such as Runnable, but it is an easy interface to use when demonstrating object expressions.

Putting it Together

Here is a complete program that shows off all three nested classes and the output.

package ch4.nestedclasses

/**
 * The Restaurant class is using the Builder pattern. It has a private
 * constructor. Inside of the Restaurant class is a RestaurantBuilder class. The
 * RestaurantBuilder has access to Restarant's private constructor, but not it's properties
 */
class Restaurant private constructor(val name: String, val address: String) {

    class RestaurantBuilder {
        var name: String = ""
        var address: String = ""

        fun addName(name: String): RestaurantBuilder {
            this.name = name
            return this
        }

        fun addAddress(address: String): RestaurantBuilder {
            this.address = address
            return this
        }

        fun build(): Restaurant = Restaurant(name, address)
    }

    override fun toString(): String = "$name, $address"
}

/**
 * DeliveryRoute has a Stop inner class. Since Stop is an inner class, Stop has
 * access to both the properties and methods of DeliveryRoute
 */
class DeliveryRoute {
    var currentStop: Stop?

    init {
        currentStop = Stop("Bobs",
                Stop("Jimmys",
                        Stop("Teddys",
                                Stop())))
    }

    inner class Stop(val name: String? = null, val next: Stop? = null) {
        fun advance(): Stop? {
            //CurrentStop is a property in DeliveryRoute
            currentStop = currentStop?.next
            return currentStop
        }

        override fun toString(): String {
            return this.name ?: "End of Route"
        }
    }
}

fun addAction(e: Runnable) {
    println(e.toString())
}

fun main(args : Array<String>){
    val builder = Restaurant.RestaurantBuilder()
    val bobsBurgers = builder
                        .addAddress("104 N Main")
                        .addName("Bob's Burgers")
                        .build()
    println(bobsBurgers)

    val route = DeliveryRoute()
    var stop : DeliveryRoute.Stop? = route.currentStop

    while(stop != null){
        println(route.currentStop)
        stop = stop.advance()
    }

    addAction(object: Runnable{
        override fun run() {
            TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
        }

        override fun toString(): String = "Object expression toString()"

    })
}

This is the output when run.

Bob's Burgers, 104 N Main
Bobs
Jimmys
Teddys
End of Route
Object expression toString()

Kotlin Abstract Classes

Abstract classes provide developers a place to provide an abstraction point while still being able to include common functionality. In a manner of speaking, an abstract class can be thought of as an unfinished class. It depends on subclasses to finish the class by defining the final behavior of the class. Since abstract classes serve as base classes for concrete classes, we can use them for polymorphism purposes.

Let’s consider a problem that uses abstract classes. We have two cooks. One cook is Bob and he makes burgers. The other cook is Jimmy and he makes pizza. Both cooks have names and own their own restaurants. They also cook food. So in this sense, they both have a lot in common. Although both cooks have the same behavior, cook(), they both cook different things. Bob cooks burgers while Jimmy cooks pizza.

We don’t want to define two classes that are almost completely the same. That would be a really bad practice because then a change in both classes would need to get updated in both places. We also would not be able to define methods that could use both classes without function overloading. In other words, our code would become highly coupled to the implementation of each cook instead of being able to use both cooks generally.

Abstract Class

Here is our Cook class that defines properties and behaviors for both Bob and Jimmy.

abstract class Cook(val name : String, val resturant : String){

    /**
     * This method doesn't have a body.
     * Child classes must define the cook() method
     * or they too must be abstract
     */
    abstract fun cook() : String

    override fun toString(): String {
        return this.name
    }
}

Classes are made abstract in Kotlin by adding the abstract keyword in front of the class keyword. We will never be able to create an instance of the Cook class due to it being marked abstract. Since our cooks cook different meals, we leave the cook method undefined by marking it as abstract as well. Abstract methods are open by default in Kotlin since they have to be overridden by definition. Since we have an abstract cook() method in Cook, we are forcing all subclasses to define this behavior.

Bob

Bob is a Cook so it makes sense that he subclasses Cook. By extending Cook, we are saying that Bob cooks. However, the Cook class doesn’t say what he cooks or how he cooks. We need Bob to extend Cook and define the cook() method.

class Bob : Cook("Bob Belcher", "Bob's Burgers") {

    override fun cook(): String {
        return "Chorizo Your Own Adventure Burger"
    }
}

Since Bob is a cook, he has a name and a restaurant. He not only cooks, but we now know how Bob cooks. Since Bob is a child class of Cook, he can be assigned to any Cook variable.

Jimmy

Jimmy is similar to Bob but he cooks pizzas.

class Jimmy : Cook("Jimmy Pesto", "Petso's Pizzeria"){
    override fun cook(): String {
        return "Boring Pizza"
    }
}

Using Abstract Classes

Now that we have Cook, Bob, and Jimmy defined, we are free to use them in our program. Let’s write a function that takes a Cook, says hello, and tells us what they cook.

fun sayHello(c : Cook){
    println("Hello! My name is ${c.name} and I cook ${c.cook()}")
}

Since Cook has a name property and a cook() method, the compiler knows that its safe to call cook() on any cook. Which version of the cook() method gets called depends on the runtime type of Cook. Regardless of who is the cook, the program will still work properly.

The sayHello function is said to be loosely coupled to the Cook class. Although we can only actually make Bob or Jimmy objects, the Cook class provides a degree of indirection for developer purposes. If we add other cooks, later on, the sayHello function can utilize those objects as well because they are all members of a common supertype.

Putting it Together

Use abstract classes when

  • You need an abstraction point
  • You have one or more child classes that have common behavior and properties
  • You need to declare behavior but need to define it in child classes

Here is an example program that ties everything together

package ch4.abstractclasses

abstract class Cook(val name : String, val resturant : String){

    /**
     * This method doesn't have a body.
     * Child classes must define the cook() method
     * or they too must be abstract
     */
    abstract fun cook() : String

    override fun toString(): String {
        return this.name
    }
}

class Bob : Cook("Bob Belcher", "Bob's Burgers") {

    override fun cook(): String {
        return "Chorizo Your Own Adventure Burger"
    }
}

class Jimmy : Cook("Jimmy Pesto", "Petso's Pizzeria"){
    override fun cook(): String {
        return "Boring Pizza"
    }
}

fun sayHello(c : Cook){
    println("Hello! My name is ${c.name} and I cook ${c.cook()}")
}

fun main(args : Array<String>){
    val bob = Bob()
    val jimmy = Jimmy()

    sayHello(bob)
    sayHello(jimmy)
}

We get this output when run.

Hello! My name is Bob Belcher and I cook Chorizo Your Own Adventure Burger
Hello! My name is Jimmy Pesto and I cook Boring Pizza