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
Advertisements

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

Kotlin Casting

Kotlin is a strongly typed language and as such, the compiler only allows developers to assign values to variables of the correct type. What that means in English is that only whole numbers can be assigned to type Int, Strings can only be assigned to variables of type String, and so on. Attempting to assign the number three to a String variable results in a compiler error.

The typing system is incredibly advantageous and helps reduce many bugs that are known to happen in weakly typed languages. Developer tools can also more easily examine variables in strongly typed languages and provide code suggestions or check for incompatibilities. For example, when passing a variable to a function, both the compiler and the IDE will issue errors if the variable is incompatible with the method signature.

Types in strongly typed languages can’t be converted readily. In order to convert one type of object into another type, we have to use a technique called casting. Casting tells the compiler that a conversion is OK to make and we are accepting the risk of converting from one data type into another type. Let’s begin with a few classes that give us an idea of what we are working with here.
Casting
Now let’s begin with some examples of casting between different types.

//This is an upcast. Any is the supertype of all Kotlin classes so it's
//acceptable to assign a Bob object to an Any reference
val bob : Any = Bob()

//Now let's downcast Bob to GrillCook. We use the is operator for this task.
if (bob is GrillCook){
    //We can now use the bob varaible as if it were GrillCook rather than Any
    //This is because the compiler now knows that bob is at least a GrillCook
    println("Bob is a GrillCook")
}

The above example shows upcasting and downcasting. When we created the bob variable, we declared its type to be Any. Any is the base class of all Kotlin objects, so it’s complete acceptable to treat a Bob object as if it’s an Any. Later on, we wish to cast Bob from any Any object into a GrillCook.

The compiler has no way to verify if Bob is a GrillCook. It only knows that Bob is any Any object at this point. To make change Bob from Any into GrillCook, we have to perform a downcast. That’s done in the if statement with if (bob is GrillCook. This statement checks the type of Bob at runtime and if Bob is a GrillCook, it will convert Bob from Any to GrillCook. The compiler will treat Bob like a GrillCook inside of the if block because it knows that if the is test succeeded, Bob must be a GrillCook.

We can also check if an object isn’t a certain kind of object. Let’s consider this code example.

val linda : Any = Linda()

if (linda !is GrillCook){
    //Linda will still be treated as Any because the compiler only
    //knows that she isn't the grill cook
    println("Linda is not the GrillCook)
}

When we add an exclamation point in front of is to make !is, we are saying to check if the object isn’t a certain kind of object. In this case, Linda does not extend GrillCook, so she’s not of type GrillCook. We proceed into the if block but Linda is still treated as type Any. The compiler only knows that she isn’t a GrillCook, but it still doesn’t know exactly what Linda is other than Any.

We can use the when expression for casting also. Let’s take a look at Gene.

val gene : Children = Gene()

when (gene) {
    //This line runs if gene is Gene
    is Gene -> println("Gene is Gene")

    //This line would only run if gene is Tina (he's not)
    is Tina -> println("Gene is Tina")

    //This line would only run if gene is Louise (he's not)
    is Louise -> {
        println("Gene is Louise")
    }
}

In the case of the when statement, if the gene variable, which if of type Children, is Gene, then the is Gene -> code is executed. The gene variable will be treated as type Gene for the duration of the block because once again, the compiler knows that the variable has to be Gene at that point. The other two clauses, is Tina -> and is Louise ->, do not execute in this example. This is because the gene variable is not either Tina or Louise so the is check returns false for those two conditions.

So far, all of the casts we have performed are known as safe casts. They are safe because we have verified the type of the object at runtime and only performed the cast after it was safe to do so. However, we can perform unsafe casts. When we perform an unsafe cast, the compiler will usually issue a warning, but it will allow us to make the cast.

val teddy : Any = GrillCook() as Children

This cast will always result in a ClassCastException. That’s because GrillCook is not a Children object.

Putting it Together

package ch1.casting

open class GrillCook

class Bob : GrillCook()

open class Wife

class Linda : Wife()

open class Children

class Tina : Children()

class Gene : Children()

class Louise : Children()

fun main(args : Array<String>){
    //These are all upcasts. Since our variables are of the base type
    //we can safely assign them to child objects without the need to cast.
    val bob : Any = Bob()
    val linda : Any = Linda()
    val gene : Children = Gene()

    //This is a safe form of down casting.
    //If Bob is in fact a GrillCook, the next line will print
    //"Bob is a GrillCook"
    if (bob is GrillCook){
        //Going forward, the compiler will treat Bob as a GrillCook object
        //Because it now knows that Bob is a GrillCook

        println("Bob is a GrillCook")
    }

    //This is the reverse of what was shown above.
    //The line inside of the if statement only runs when linda
    //is not a GrillCook. Note that the linda variable is still treated
    //as type Any because the compiler only knows that Linda isn't a GrillCook
    if (linda !is GrillCook){
        println("Linda is not the GrillCook")
    }

    //We can also cast with the when expression
    when (gene) {
        //This line runs if gene is Gene
        is Gene -> println("Gene is Gene")

        //This line would only run if gene is Tina (he's not)
        is Tina -> println("Gene is Tina")

        //This line would only run if gene is Louise (he's not)
        is Louise -> {
            println("Gene is Louise")
        }
    }

    //This is an example of an unsafe cast. It forces the compiler
    //to make the conversion, but at runtime, we get a ClassCastException.
    //Notice that the compiler does issue a warning here, but since this statement
    //is valid, the code will compile
    val teddy : Any = GrillCook() as Children
}

When run, we get the following output.

Bob is a GrillCook
Linda is not the GrillCook
Gene is Gene
Exception in thread "main" java.lang.ClassCastException: ch1.casting.GrillCook cannot be cast to ch1.casting.Children
	at ch1.casting.CastingKt.main(Casting.kt:58)

References

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

Kotlin Overriding Methods

Method overriding is an important part of polymorphism. When we override methods, we are redefining the behavior of a base class method in a child class. Doing so allows us to create specialized cases of our classes that still work with existing code. At runtime, the JVM will use the proper implementation of a method depending on the object’s type.

Kotlin has it’s own twist on overriding classes and methods. OOP has an issue known as “fragile super-classes” where changes in base classes break child classes due to developers not fully considering how changes in a base class may affect a child class. Due to the fragile super-class issue, Kotlin disables both inheritance and method overriding by default. In order to use either, we must signal to the compiler that we can extend a class or override its methods by using the open keyword. Open classes and methods force developers to consider the possibility that a class may be extended or it’s methods or overridable.

Here is an example of an open class.

/**
 * Base class. This class has to be marked as open to allow
 * inheritance in Kotlin
 */
open class GrillCook {

    /**
     * We also have to mark our methods as open to allow
     * for overriding
     */
    open fun print() {
        println("Grill Cook is the Master")
    }
}

Our GrillCook class can be extended because we have included the open keyword in front of the class keyword. Likewise, our print() method is also marked as open can be therefore overridden by child classes. So our next class, BobBelcher takes advantage of the opportunity.

/**
 * Child class that override print in the base class. Notice that this
 * class declares it's name property as open, which means child classes
 * can override the property also
 */
open class BobBelcher(open val name : String) : GrillCook(){

    //override keyword signals that we are overriding a super class method
    override fun print(){
        println("$name is the Master")
    }
}

BobBelcher overrides the print() method and changes what is printed to the console. Since this class has a name property, we print “$name is the Master” rather than “Grill Cook is the Master”. To override print(), Bob has to have the keyword override in the method signature. This is to prevent another common OOP problem where developers intended to override a function but accidentally overloaded it instead, usually due to some sort of typo.

Kotlin supports property overriding if properties are marked as open. The name property on BobBelcher is marked as open, which means child classes can also override the name property. This is what LindaBelcher does.

/**
 * LindaBelcher is a child class of BobBelcher and overrides the
 * name property to set it to Linda rather than some other string
 */
class LindaBelcher : BobBelcher("") {
    override val name = "Linda" //Use the override keyword to override a property

    override fun print() {
        //Use the print implementation found in BobBelcher
        super.print()
        println("Alright!!!")
    }
}

LindaBelcher overrides the name property found in BobBelcher and returns Linda. That means that no matter what String gets passed to the BobBelcher constructor invocation in LindaBelcher, we end up getting “Linda” when we use the name property. It’s also worth noting that Linda has a call to super.print() inside of the print() function. Using the super.print() tells the compiler to call the implementation of print() found in Bob. Then Linda’s print function completes by printing “Alright!!!” to the console.

Putting it Together

Here is a complete program that demonstrates method overriding in Kotlin.

package ch1.overriding

/**
 * Base class. This class has to be marked as open to allow
 * inheritance in Kotlin
 */
open class GrillCook {

    /**
     * We also have to mark our methods as open to allow
     * for overriding
     */
    open fun print() {
        println("Grill Cook is the Master")
    }
}

/**
 * Child class that override print in the base class. Notice that this
 * class declares it's name property as open, which means child classes
 * can override the property also
 */
open class BobBelcher(open val name : String) : GrillCook(){

    override fun print(){
        println("$name is the Master")
    }
}

/**
 * LindaBelcher is a child class of BobBelcher and overrides the
 * name property to set it to Linda rather than some other string
 */
class LindaBelcher : BobBelcher("") {
    override val name = "Linda"

    override fun print() {
        //Use the print implementation found in BobBelcher
        super.print()
        println("Alright!!!")
    }
}

fun main(args : Array<String>){
    val grillCook = GrillCook()
    val bob = BobBelcher("Bob")
    val linda = LindaBelcher()

    println("Using grillCook::print")
    grillCook.print()

    println("\nUsing BobBelcher::print")
    bob.print()

    println("\nUsing LindaBelcher::print")
    linda.print()
}

This is the output when run.

Using grillCook::print
Grill Cook is the Master

Using BobBelcher::print
Bob is the Master

Using LindaBelcher::print
Linda is the Master
Alright!!!

It works because at runtime, the JVM knows to use the proper implementation of print() based on the objects type at runtime. This is why all three objects print different outputs when print is called.

References

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