Kotlin Lists

Lists are one of the most common data structures in computer programming because they allow us to group related data into a single object. For example, we may make a list of each letter of the alphabet. We would need 26 variables, one for each letter, without a list. However, a list lets us create a single object that holds all 26 letters of the alphabet.

Kotlin has two forms of lists. One is a read-only list and the other is a mutable list. The read-only list is created once and only allows operations that do not change the list. For example, we are free to look at each element in such a list, but we may not add or remove elements or sort the list. Such operations would change the list. We are free to iterate through a read-only list and look for elements that match certain criteria, but we may not reassign certain members of the list.

Mutable lists support all operations found in a read-only list be we may also modify the list itself. So we are free to sort a mutable list, add items to it, or remove items. We may even reassign elements at certain indexes. Both styles of lists have methods that facilitate conversion between one form of a list to the other.

Read Only Lists

Creation

We create a read only list using the listOf() function and passing any number of arguments to the function.

val family = listOf("Bob", "Linda", "Tina", "Gene", "Lousie")
val numbers = listOf(1, 2, 3, 4)
val anyList = listOf<Any>(1, "Linda", 1.0)
val nullList = listOf<String?>("Teddy", "Mort", null)

The Kotlin compiler is usually able to figure out the type of the list, but in cases of mixed types, we may need to explicity specify the type of objects the list can hold. Lists hold non-null values by default, so if we need the list to store nulls, we need to explicitly tell the list to hold nulls by specifying the type followed by a ‘?’ mark.

Accessing items

Lists overload the index operator [] so we access elements in the list using the index operator.

println(family[0]) //prints Bob

Checking Membership

We can check if an item exists in the list by using the in keyword.

val hasBob = "Bob" in family
val hasTeddy = "Teddy" in family
println(hasBob) //prints true
println(hasTeddy) //prints false

Finding the Max Item

Lists have a max() method that returns the max item in the list.

println(family.max()) //prints "Tina"

Last Item in a List

We can use the last() function to return the last item in a list.

pritnln(family.last()) //prints Louise

Go Through the List

Lists have a forEach method that lets you go through the list one element at a time.

family.forEach( {it -> println(it) } )

The forEach function takes a lambda expression. The “it” variable refers to the current item in the list.

Mutable Lists

Mutable Lists have the same functionality as above but also allow for operations that change the list.

Creation

Use the mutableListOf() function to create a mutable list.

val smiths = mutableListOf("Rick", "Jerry", "Beth", "Summer", "Morty")

Adding / Removing Items

We use the add() method to add an item and the remove method to remove an item.

smiths.add("Rick Clone")
smiths.remove("Rick Clone")

smiths.add("Morty Clone", 0) //insert at index 0
smiths.remove(0) //Remove element at index 0

Sorting

The mutable list also has a handy sort() method.

smiths.sort() //Now the list is sorted in alphabetical order

Conclusion

Kotlin is one of the few languages that distinguishes between read only and read write lists. Read only lists are write protected and only allow non-changing operations. Mutable lists are read and write lists and allow for mutating operations such as adding or removing items. We have demonstrated a few of the more common operations found on lists but there are many more. Please check the Kotlin documentation for more details!

Kotlin Generic Methods

Generic methods are methods that work on a variety of different type arguments. Some concrete examples of generic methods found in the Kotlin standard library are listOf(), setOf() mapOf(), with(), apply(), etc. All of these methods are compatible with any type of variable and work correctly for each type. A generic method promotes code reuse because a method can be written with a variable type argument that is later substituted with a real type by the compiler.

It’s very easy to write our own generic methods. We need only declare our type arguments inside of angle brackets after the fun keyword. Here is an example of a method that fills a MutableList with a value.

/**
 * Generic method that fills a MutableList with a value. The type argument is declared
 * as T which is later substituted with real types by the compiler.
 */
fun <T> fillList(list : MutableList<T>, value : T, length : Int){
    for (i in 0..length){
        list.add(value)
    }
}

Our fill list isn’t anything fancy, but it is very powerful. The fillList() function is compatible with any type of variable because it uses a generic type, T, as a type argument. Later on, the Kotlin compiler will substitute T with Int, String, or any other type that we need for our purposes.

It is worthwhile to note that all of the typed parameters in fillList() are the same type. In other words, the list variable and the value variable both have to be the same type of argument. We are not allowed to pass in a MutableList<String> along with a value of Int. That would result in a compiler error. If we need multiple type arguments, we need to declare them inside of the angle brackets found after the fun keyword.

We can use our fillList() function like any other function when ready. Here is an example of using our function.

package ch6.genericmethods

/**
 * Generic method that fills a MutableList with a value. The type argument is declared
 * as T which is later substituted with real types by the compiler.
 */
fun <T> fillList(list : MutableList<T>, value : T, length : Int){
    for (i in 0..length){
        list.add(value)
    }
}

fun main(args : Array<String>){
    val intList = mutableListOf<Int>()
    val stringList = mutableListOf<String>()

    fillList(intList, 100, 5)
    fillList(stringList, "Bob", 5)
    //fillList(intList, "Bob", 5) Not OK! intList is MutableList<Int> while Bob is a String

    println(intList)
    println(stringList)
}

When run, this program outputs the following.

[100, 100, 100, 100, 100, 100]
[Bob, Bob, Bob, Bob, Bob, Bob]

Kotlin Generic Classes

Generic classes offer a way for developers to create a class that works with multiple types while retaining type safety. A list is one of the most commonly used generic classes in Kotlin because it allows developers to group a bunch of related objects together in a sequence. The list also works with different types of variables.

//Declare a list of Strings
val stringList = listOf("str1", "str2", "str3")

//Declear a list of ints
val intList = listOf(1, 2, 3, 4)

Both types of lists are created using the listOf() of function. The compiler knows that stringList is a list of Strings because the values passed to listOf are strings. Likewise, the compiler can determine that intList is a list of ints because the values passed are ints.

The listOf() function and the object it returns are generic. In other words, they function and the object are the same for both cases of listOf() as opposed to function overloading which defines different functions based on the type of object. Using generics allows for code reuse because we only write a class or a function once and the compiler substitutes the generic type for a real type when needed.

We will use the following Cook class as an example to demonstrate how to write our own generic classes.

data class Cook<T>(
        var id : T, //Generic property. Can be any type
        var name : String)

The Cook class looks like any other class, except for this <T> that appears after the name of the class. That is the type argument and T is a variable for the Type. The first property of Cook, id, is of type T. Later on, the T parameter can become a String, Int, or basically any valid Kotlin type, including other classes. This allows the Cook class to be flexible about what type of values are stored in the id property.

We need to supply the type of id when we create an instance of Cook.

//bob will be a Cook<String> because the compiler knows that "Bob's Burgers" is a string
val bob = Cook("Bob's Burgers", "Bob")

//jimmy will be a Cook<Int> because the compiler knows that 10001 is an Int
val jimmy = Cook(10001, "Jimmy")

In bob’s case, the compiler knows that “Bob’s Burgers” is a String. So the id in Cook becomes a String and takes on the value of “Bob’s Burgers”. Going forward, bob.id will hold Strings. In jimmy’s case, the compiler knows that 10001 is an Int type, so the id property becomes an Int and jimmy.id will only hold ints.

Using generics protects us from type errors later on. Suppose we were to try assigning a number to bob’s id property.

bob.id = 1000 //BAD! This won't compile

The compiler will refuse to compile the above code because when we created Bob, we used a String for the id property. The compiler check is good because it protects us against ClassCastExceptions at runtime. Since Cook is a generic class, we only need to write the class once and we can use T to represent anytime that is needed for the ID property.

Putting it Together

Here is a working program that shows our Cook generic class in action.

package ch6.genericclasses

data class Cook<T>(
        var id : T, //Generic property. Can be any type
        var name : String)

fun main(args : Array<String>){
    //bob will be a Cook<String> because the compiler knows that "Bob's Burgers" is a string
    val bob = Cook("Bob's Burgers", "Bob")

    //jimmy will be a Cook<Int> because the compiler knows that 10001 is an Int
    val jimmy = Cook(10001, "Jimmy")

    println(bob)
    println(jimmy)

    //The next two lines do not compile
    //bob.id = 1000 (Remember, id is a String in Bob's case and 1000 is an Int so this doesn't compile)
    //jimmy.id = "Jimmy's Pizzeria" (In jimmy's case, id is an Int and "Jimmy's Pizzeria" is a string so this doesn't compile)
}

Output

Cook(id=Bob's Burgers, name=Bob)
Cook(id=10001, name=Jimmy)

Kotlin Composition

In the real world, complex systems are built from simple parts. For example, we might decide to make a kitchen. A kitchen would have a refrigerator, a stove, a dishwasher, and countertops. The stove would consist of burgers, switches to turn the burners on and off, and gas values. The gas valve may have a cutoff mechanism and a pressure regulator.

All of these complex objects are created by smaller and simpler components. It is also worth noting that the Kitchen has a stove. The Stove has a burner, and so on. In OOP, the creation of complex objects from simpler components is known as composition. We create classes that are of small concern and scope and then combine them into larger classes.

Kotlin seems to favor composition by the way. You may notice that classes are final by default and that developers need to mark classes as open prior to using inheritance. Likewise, methods need to be marked as open before they can be overridden. Kotlin has a delegation mechanism where the compiler can create delegate methods when using another object. Finally, our interfaces can even have properties and default behaviors. Even if composition wasn’t the intention, all of these mechanisms in the language tend to steer a developer towards composition.

All of which is desirable. We could create classes and add more features to them by using inheritance. Nevertheless, classes in Kotlin are only allowed to have one parent class which means we can’t use two or more parent classes to create a single type. Furthermore, we could end up creating instability in the code as changes in base classes could end up breaking child classes. Finally, unit testing classes become more difficult because do we continue to test superclass behavior in child classes to prevent regressions?

Composition addresses many such issues. Complexe classes use simple objects to break down a problem into manageable scopes. Classes that implement interfaces become loosely coupled and allow for additional maintainability. We can easily unit test small classes and then move onto to unit testing larger classes. Finally, we can swap or add components as needed to keep the code maintainable.

Inheritance describes an is-a relationship. As in, a Truck is-a a vehicle. Composition describes a has-a relationship as in a Cook has-a spatula. We certainly would not want to extend a Cook from a Spatula or a Spatula from a Cook. The relationship would make no sense. Would we really want methods that target Cooks also be able to accept Spatula objects through polymorphism? Should all Cooks get the same properties and behaviors as a Spatula?

We solve such problems by having Cooks own a spatula. Both classes are distinct entities that have different concerns. Our Cooks should not only be able to posses Spatulas but also other cooking utensils as needed. Each utensil may be used for a different purpose. For example, the cook uses the thermometer for checking food temperature and a spatula for flipping a burger patty.

It’s not difficult to model such a relationship in Kotlin. We only need to use classes and interfaces to put such a relationship together. Let’s start with a Utensil interface.

interface Utensil {
    val name : String

    fun interact(f : Food)
}

Now we only need to create two classes that implement the Utensil interface. Here is the thermometer.

class Thermometer : Utensil {
    override val name: String
        get() = "Thermometer"

    override fun interact(f: Food) {
        println("The ${f.name} has a temperature of 160")
    }
}

Followed by the Spatula

class Spatula : Utensil{
    override val name: String
        get() = "Spatula"

    override fun interact(f: Food) {
        println("Flipping the ${f.name}")
    }
}

Our Cook class can use either tool.

class Cook(var utensil: Utensil) {
    val name = "Bob"

    fun cook() : Food {
        val burger = object : Food {
            override val name: String
                get() = "Burger"
        }
        println("Bob is cooking")
        println("Now Bob is using the ${utensil.name}")
        utensil.interact(burger)
        println("Bob is done cooking")
        return burger
    }
}

Since Cook has a Utensil property, he is free to use either the Thermometer or the Spatula. This flexibility would have been almost impossible to model using inheritance and equally difficult to maintain. However, since our Cook possesses Utensil objects, we can freely swap out different utensils as needed.

Putting it Together

Use composition when you need to create complex objects using simple objects. Try and remember that each object should have its own unique concern. Finally, use interfaces when possible to avoid tight coupling between classes. The following program demonstrates a program that uses compositions and interfaces to create a complex object.

package ch5.interfaces.composition

/**
 * We shouldn't tie any object to one specific kind of Food
 */
interface Food {
    val name : String
}

/**
 * Likewise, we shouldn't tie any one specific object to a specific
 * Utensil. We should always try and think generally.
 */
interface Utensil {
    val name : String

    fun interact(f : Food)
}

/**
 * The Spatula is a Utensil
 */
class Spatula : Utensil{
    override val name: String
        get() = "Spatula"

    override fun interact(f: Food) {
        println("Flipping the ${f.name}")
    }
}

/**
 * The Thermmometer is another Utensil
 */
class Thermometer : Utensil {
    override val name: String
        get() = "Thermometer"

    override fun interact(f: Food) {
        println("The ${f.name} has a temperature of 160")
    }
}

/**
 * Cook is a complex object that is made up of Utensils and outputs Food
 */
class Cook(var utensil: Utensil) {
    val name = "Bob"

    fun cook() : Food { //Notice the return type is Food
        val burger = object : Food {
            override val name: String
                get() = "Burger"
        }
        println("Bob is cooking")
        println("Now Bob is using the ${utensil.name}")
        utensil.interact(burger)
        println("Bob is done cooking")
        return burger
    }
}

fun main(args : Array<String>){
    val bob = Cook(Thermometer())
    bob.cook()
    println()

    bob.utensil = Spatula()
    bob.cook()
}

Output

Bob is cooking
Now Bob is using the Thermometer
The Burger has a temperature of 160
Bob is done cooking

Bob is cooking
Now Bob is using the Spatula
Flipping the Burger
Bob is done cooking

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