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

Advertisements

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

Kotlin Covariant Return types

It’s legal to substitute a child class as the return type of a base class when overriding a function. Doing so allows us to avoid unnecessary casting. Let’s look at an example.

abstract class Cook(val name : String) {

    //Abstract method that returns Cook
    abstract fun copy() : Cook
}

class Bob(name : String) : Cook(name) {

    /**
     * Notice how this method changes the
     * return type from Cook to Bob. This is legal
     * because Bob is a child class of Cook.
     *
     * This is called covariant return types
     */
    override fun copy(): Bob {
        return Bob(this.name)
    }
}

In this example, we have a base class Cook that has an abstract copy() method. The copy() method returns a Cook object. However, Bob overrides copy() and returns Bob. The difference may look small, but when used we do not need to downcast Cook objects back to Bob.

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

    //No casting required since copy() returns a Bob
    val bobCopy : Bob = bob.copy()
    val cookCopy : Cook = bob.copy()

    println(bob.name)
    println(bobCopy.name)
    println(cookCopy.name)
}

If we had the copy() method in Bob return Cook rather than Bob, we would need to down cast.

class Bob(name : String) : Cook(name) {

    //This verion returns the Base Type
    override fun copy(): Cook {
        return Bob(this.name)
    }
}

val bobCopy : Bob = bob.copy() as Bob //Now a cast is required

So although we could have Bob.copy() return Cook, we now have to immediately downcast the result back into a Bob object. This doesn’t really make a lot of sense for a copy() method, which is why it’s acceptable to override a method by returning a child class type when needed.

Putting it Together

Here is an example program that shows off covariant return types.

package ch1.overriding

abstract class Cook(val name : String) {

    //Abstract method that returns Cook
    abstract fun copy() : Cook
}

class Bob(name : String) : Cook(name) {

    /**
     * Notice how this method changes the
     * return type from Cook to Bob. This is legal
     * because Bob is a child class of Cook.
     *
     * This is called covariant return types
     */
    override fun copy(): Bob {
        return Bob(this.name)
    }
}

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

    //No casting required since copy() returns a Bob
    val bobCopy : Bob = bob.copy()
    val cookCopy : Cook = bob.copy()

    println(bob.name)
    println(bobCopy.name)
    println(cookCopy.name)
}

Here is the output when run

Bob
Bob
Bob

Kotlin Constructors

Many OOP languages have special code that is used to initialize a class to a valid state. Such code is referred to as a constructor method. The constructor runs when an instance of a class is created.

Like all methods, constructors can have zero or more parameters. Let’s consider the constructor found in the ArrayList class found in java.util.collections.

ArrayList<?> list = new ArrayList<>(); //Default constructor
ArrayList<?> list2 = new ArrayList<>(list); //Secondary constructor

The above Java code snippet demonstrates multiple constructor. The first constructor is called the default constructor and accepts no arguments. It creates an empty ArrayList object. The other constructor takes an existing collection object and automatically adds all objects contained in the collection passed into the constructor into the new list. Let’s walk through the various forms of constructors we can define in Kotlin.

Default Constructors

When no constructor is specified, Kotlin will supply an empty no argument constructor.

class ChalkBoard {
    val message = "New Baconings"
}

val chalkBoard = ChalkBoard()
println(chalkBoard.message)

The ChalkBoard class has no constructor. When we create a ChalkBoard object, we just use the () for the default constructor.

Constructor with Required Parameters

We can also define constructors that force us to use valid data.

class Bob(val position : String)

val bob = Bob("Cook")
println(bob.position)

Notice how the Bob class has a position parameter of type String. When we create an instance of Bob, we have to supply a String to the constructor. There is no default constructor for Bob. Since we used the val keyword in front of the position argument, Kotlin created a position property on Bob. This lets use print Bob’s position in the println statement.

Constructor with Initialization Block

We aren’t limited to just setting properties in a constructor. Kotlin also lets us create an initialization block that runs immediately after our properties have been set.

class Linda(val position : String){
    //This is the initialization block that runs when an instance
    //of this class is created
    init {
        check(position.isNotBlank(), { "Linda needs a job!" })
        println("Linda was created with $position")
    }
}

val linda = Linda("Cook's wife")
println(linda.position)

The above code still set’s Linda’s position property, but when the constructor runs, it will also print “Lnda was created with [specified position]” because of the init block found immedialy after the constructor as long was position is not an empty String. If position is blank, the init block would throw an exception with the message “Linda needs a job!”. Init blocks can do other things besides validation, but validation is certainly a common use case of the init block.

Property Initializers

Kotlin also provides property initialization blocks. Let’s consider Gene.

class Gene(position : String){
    //This is the property initialization section
    val beefSquash = position.toUpperCase()
}

val gene = Gene("Beefsquash")
println(gene.beefSquash)

Gene has a constructor that takes a position, but notice that there is no val or var keyword prior to position. Since the var/val keyword is missing, Kotlin does not generate a position property for Gene. On the first line of the class body, we have val beefSquash = position.toUpperCase(). The code creates a beefSquash property on Gene and sets it to the upper case value of position. This lets us create properties outside of the constructor and initialize them to the arguments found in the constructor.

Access modifiers on Constructors

A common OOP pattern is to create private constructors and use factory or builder objects to create an instance of a class. Our Tina class shows a stripped down example of the factory pattern.

class Tina private constructor(val position : String){

    //We have to use a companion object to make getInstance() visible
    companion object {
        //We can only use the constructor from within Tina because it's private
        fun getInstance(position : String) = Tina(position)
    }
}

//Can't do this because Tina's constructor is private
//val tina = Tina("Cook");

//We can do this
val tina = Tina.getInstance("Itchy Grill Cook")
println(tina.position)

Whenever we need to add access modifiers or annotations to a constructor, the constructor keyword is required. In this case, we have private constructor in front of the parameters of Tina’s constructor. Only code found within the Tina class may use the constructor because private restricts the visibility of the constructor. Tina objects can only be created by invoking the getInstance() method which creates and returns a Tina object.

Multiple Constructors

Koltin doesn’t limit us to using only constructor. We are actually free to have as many constructors as we need.

class Louise (val position: String){

    //Calling this(...) after the colon but before the { will invoke the
    //first constructor
    constructor(position: String, age : Int): this(position){
        println("Inside secondary constructor with $position and $age")
    }
}
//Call the single argument constructor
val lousieSingle = Louise("Bunny Ears")

//Call the secondary constructor
val louise = Louise("Bunny Ears", 10)
println(louise.position)

Louise has a regular constructor, but inside of the class body, we can define additional constructors by using the constructor keyword and then specifying our arguments. We are free to reuse code between our constructors by using the this keyword followed by the parameters of the constructor we wish to use. Kotlin will use function overloading to resolve to the correct constructor (or issue a compiler error if one can’t be found). In the example, we see two different ways to create a Louise object. We can use her primary constructor, or her secondary constructor, which also print “Inside secondary constructor with $position and $age” to the console.

Optional Arguments

The final constructor technique involves using optional arguments to specify a default value if a parameter is ommitted. Let’s take a look at Teddy.

class Teddy (val position : String = "customer", val favoriteFood : String = "burger")

val teddyDefault = Teddy()
println(teddyDefault.position + ", " + teddyDefault.favoriteFood)

val teddyArguments = Teddy(position = "Best Customer", favoriteFood = "Burger of the Day")
println(teddyArguments.position + ", " + teddyArguments.favoriteFood)

val teddyFood = Teddy(favoriteFood = "Burger of the day")
println(teddyFood.position + ", " + teddyArguments.favoriteFood)

val teddyPosition = Teddy(position = "Best Customer")
println(teddyPosition.position + ", " + teddyArguments.favoriteFood)

We actually have four constructors for Teddy. The first one is a no argument constructor that initializes Teddy’s poistion to customer and his favoriteFood to burger. The other constructor let’s us specify Teddy’s posistion and his favoriteFood. The third constructor let’s use specify Teddy’s favoriteFood and use his default position. Finally we can use the constructor that specifies Teddy’s position, but use the default for favorite foods.

Putting it all Together

package ch1.constructors

class ChalkBoard {
    val message = "New Baconings"
}

/**
 * Kotlin class with an empty constructor
 */
class Bob(val position : String)

/**
 * Kotlin class with initialization block
 */
class Linda(val position : String){
    //This is the initialization block that runs when an instance
    //of this class is created
    init {
        check(position.isNotBlank(), { "Linda needs a job!" })
        println("Linda was created with $position")
    }
}

/**
 * Kotlin class with a property initializer
 */
class Gene(position : String){
    //This is the property initialization section
    val beefSquash = position.toUpperCase()
}

/**
 * Kotlin class with private constructor
 */
class Tina private constructor(val position : String){

    //We have to use a companion object to make getInstance() visible
    companion object {
        //We can only use the constructor from within Tina because it's private
        fun getInstance(position : String) = Tina(position)
    }
}

/**
 * Kotlin class with multiple constructors
 */
class Louise (val position: String){

    //Calling this(...) after the colon but before the { will invoke the
    //first constructor
    constructor(position: String, age : Int): this(position){
        println("Inside secondary constructor with $position and $age")
    }
}

/**
 * Kotlin class that has a constructor with optional arguments. We can use one,
 * both, or none of the arguments when we create an instance of Teddy
 */
class Teddy (val position : String = "customer", val favoriteFood : String = "burger")

fun main(args : Array<String>){
    val chalkBoard = ChalkBoard()
    println(chalkBoard.message)

    val bob = Bob("Cook")
    println(bob.position)

    val linda = Linda("Cook's wife")
    println(linda.position)

    val gene = Gene("Beefsquash")
    println(gene.beefSquash)

    val tina = Tina.getInstance("Itchy Grill Cook")
    println(tina.position)

    val louise = Louise("Bunny Ears", 10)
    println(louise.position)

    val teddyDefault = Teddy()
    println(teddyDefault.position + ", " + teddyDefault.favoriteFood)

    val teddyArguments = Teddy(position = "Best Customer", favoriteFood = "Burger of the Day")
    println(teddyArguments.position + ", " + teddyArguments.favoriteFood)

    val teddyFood = Teddy(favoriteFood = "Burger of the day")
    println(teddyFood.position + ", " + teddyArguments.favoriteFood)

    val teddyPosition = Teddy(position = "Best Customer")
    println(teddyPosition.position + ", " + teddyArguments.favoriteFood)

}

Here’s the output

New Baconings
Cook
Linda was created with Cook's wife
Cook's wife
BEEFSQUASH
Itchy Grill Cook
Inside secondary constructor with Bunny Ears and 10
Bunny Ears
customer, burger
Best Customer, Burger of the Day
customer, Burger of the Day
Best Customer, Burger of the Day

References

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

Kotlin Polymorphism

Polymorphism allows computer code to become contextual. In other words, a computer instruction can take on different meanings depending on situation in which the instruction is used. This is no different than how we speak. A person can use the same word, ‘there’, ‘they’re’, or ‘their’ to mean different things even though all three words are said the same way.

Kotlin supports two forms of polymorphism because it is both strongly and statically typed. The first form of polymorphism happens when the code is compiled. The other form happens at runtime. Understanding both forms of polymorphism is critical when writing code in Kotlin.

Compile Time Polymorphism

Let’s consider the following code snippet.

fun printNumber(n : Number){
    println("Using printNumber(n : Number)")
    println(n.toString() + "\n")
}

fun printNumber(n : Int){
    println("Using printNumber(n : Int)")
    println(n.toString() + "\n")
}

fun printNumber(n : Double){
    println("Using printNumber(n : Double)")
    println(n.toString() + "\n")
}

We have three functions all of which have the same name and return type (void). As a matter of fact, the only difference in the signature of these methods is the type of parameter that is used. The first printNumber accepts a Number variable. The second one accepts an Int variable, and the final one accepts a Double variable. This technique is called function overloading.

Since all functions have the same name, how do we know which one will be used when we have code such as the snippet below?

val a : Number = 99
val b = 1
val c = 3.1

printNumber(a) //Which version of printNumber is getting used?
printNumber(b) //Which version of printNumber is getting used?
printNumber(c) //Which version of printNumber is getting used?

The compiler sees three functions with the same name and knows it ultimately has to choose a version of printNumber to use on each statement. Since all three functions have the same name, the compiler turns to context clues to deduce which printNumber function should be used in each case. The variable ‘a’ is explicity declared as type Number. Since there is a version of printNumber that accepts a Number parameter, that version of printNumber is chosen.

The next variable, b, is assigned to one. The number one is an integer, and since we didn’t tell the compiler to make it a Number, it defaults to an Integer type. When printNumber(b) is called, the compiler matches to the version of printNumber that accepts type Integer. The final variable, ‘c’, is initialized to 3.1. The default type for that number is Double so the compiler chooses printNumber(Double) as the correct version to call when ‘c’ is passed as an argument.

It should be noted that when the compiler can’t correctly pick a method, it will issue an error. We are free to overload functions as long as the method signatures are unique enough for the compiler to figure out which function should be used. So for example, functions may have the same return type provided that they take different parameters or a different number of parameters. What isn’t allowed is for two functions to have the same parameters but different return types. In that case, the compiler is unable to figure out which function to use and it will issue an error.

Function overloading is a powerful technique that allows us to write flexible code. The main use case is to write functions that have different behaviors based on the type of object. Imagine writing a function that converts objects to JSON (a form of data exchange). Since every object can be different, we would need different implementations of a functions to correctly output JSON for each type of object. However, it doesn’t really make sense to write things like intToJson, arrayToJson, or carToJson. We can call of these functions by the same name: toJson, and just use polymorphism to call the proper implementation.

Runtime Polymorphism

Runtime polymorphism happens when the program is running. Since Kotlin is an OOP language, we can use classes and interfaces to refer to objects. For example, Number is a class while Integer and Double are both child classes of Number. That it is acceptable to use Integer or Double variables anywhere in our code where Number is expected.

However, things go even futher than using child classes as values for a base class. When a child class overrides a method defined in a base class, the program will use the child class’s method rather than the base class. This is called virtual methods. Let’s consider Number’s toDouble() method.

The toDouble() method is defined in Number, which means all child classes of toDouble() have some sort of an implementation of toDouble(). When we call toDouble() on an Integer, the program knows to use the toDouble() defined in Integer so that the output of toDouble() makes sense for an Integer. Likewise where we to call toDouble() on a BigDecimal object, we would also get the version of that method defined in BigDecimal.

In our code example, let’s consider summing all of our number variables.

fun sum(numbers : List<Number>) : Number {
    return numbers.sumByDouble { it.toDouble() }
}

fun main(args : Array<String>){
    val a : Number = 99
    val b = 1
    val c = 3.1

    //Using runtime polymorphism
    println("Summing all numbers")
    println(sum(listOf(a, b, c)))
}

Since the varaibles ‘a’, ‘b’, and ‘c’ are all either types of Number or child classes of Number, we can use them in the sum function. Inside of the function, we call it.toDouble() to convert the current number into a double. One of those numbers is the variable ‘b’ which is an Integer. So when b.toDouble() gets called, the version associated with Integer is used. However, when c.toDouble() gets called, the version of toDouble() associated with Double gets used.

Putting it together

Both forms of polymorphism allow for highly flexible code. Well designed computer code should be written with generalization in mind. By targeting a base class or an interface, we can reuse the same code with different types of objects by using polymorphism. Likewise, function overloading improves the readability of code and it’s maintainability because we can call a function with the same name as other functions and trust that the proper function is used depending on the context.

Example Program

/**
 * The first 3 functions, all called printNumber, demonstrate function
 * overloading. This is polymorphism that is determined at compile time.
 * Basically, the compiler knows which function to use based on the type of the
 * input parameter n. So if n is an Int, it will use printNumber(n : Int).
 * If n is a double, it will use printNumber(n : Double). For all other numbers,
 * it will use printNumber(n : Number).
 */
fun printNumber(n : Number){
    println("Using printNumber(n : Number)")
    println(n.toString() + "\n")
}

fun printNumber(n : Int){
    println("Using printNumber(n : Int)")
    println(n.toString() + "\n")
}

fun printNumber(n : Double){
    println("Using printNumber(n : Double)")
    println(n.toString() + "\n")
}

/**
 * This function shows runtime polymorphism. In this case, all objects are of type Number.
 * Number has a toDouble() method which is different for each kind of number. However, since
 * all classes that extend Number must implement toDouble(), we can trust that longs, ints, floats,
 * etc can all make the conversion to a double when needed.
 */
fun sum(numbers : List<Number>) : Number {
    return numbers.sumByDouble { it.toDouble() }
}

fun main(args : Array<String>){
    val a : Number = 99
    val b = 1
    val c = 3.1

    //Using compile time polymorphism
    printNumber(a)
    printNumber(b)
    printNumber(c)

    //Using runtime polymorphism
    println("Summing all numbers")
    println(sum(listOf(a, b, c)))
}

Kotlin Inheritance

Inheritance is a core part of Object Orientated programming that provides a powerful code reuse tool by grouping properties and behaviors into common classes (called base classes) and having unique properties and behaviors placed in specific classes that grow out from the base class (called child classes). The child class receives all properties and behavior from its parent class, but it also contains properties and behaviors that are unique to itself. Inheritance allows for specialization of software components when a component has a specific need, but also allows for generalization when using items common to the parent.

Let’s consider an example that is more specific. Suppose we have a Vehicle class that models some sort of vehicle that we can drive. We start by creating a class.

open class Vehicle(
        private val make : String,
        private val model : String,
        private val year : Int) {

    fun start() = println("Starting up the motor")

    fun stop() = println("Turning off the engine")

    fun park() = println("Parking " + toString())

    open fun drive() = println("Driving " + toString())

    open fun reverse() = println("Reversing " + toString())

    override fun toString(): String {
        return "${year} ${make} ${model}"
    }
}

We know that all Vehicles have a make, model, and year. Users of a Vehicle can start and turn off the motor. They can also park, drive, or put the Vehicle in reverse. So far so good. However, later on, we need a vehicle that can tow a camper.

We could add a tow() method to Vehicle, but would that really make sense? What if our Vehicle is a Fiat? Would we really tow a camper with a Fiat? What we need is a Truck. Thanks to Inheritance, we don’t need to write Truck from scratch. We can simply create a specialized version of a Vehicle instead.

open class Truck(make: String,
            model: String,
            year: Int,
            private val towCapacity : Int) : Vehicle(make, model, year) {
    fun tow () = println("${toString()} is towing ${this.towCapacity} lbs")
}

This code creates a Truck class based off of Vehicle. As such, the Truck still has a make, model, and year. It can also start(), stop(), drive(), park(), and reverse() just like any other vehicle. However, it can also tow things and has a towCapacity property. What we have essentially done is reused all of the code from Vehicle and just changed what was needed so that we have a new Vehicle like object that also tows things.

Of course, later on, our needs change again and we decide to go camping in the mountains. It may snow in the mountains, so in addition to being able to tow things, we may want four wheel drive. Once again, not all Trucks have four wheel drive, so we don’t want to add a four wheel drive into Truck. As a matter of fact, four wheel drive isn’t even a specific behavior. What it really does is it enhances the already existing behaviors drive and reverse.

Let’s create another child class, based off of Truck, and specialize the driving behavior.

class FourWheelDrive(make: String, model: String, year: Int, towCapacity: Int) :
        Truck(make, model, year, towCapacity) {

    var fourByFour = false

    override fun drive() {
        if(fourByFour){
            println("Driving ${toString()} in four wheel drive")
        } else {
            super.drive()
        }
    }

    override fun reverse() {
        if(fourByFour){
            println("Reversing ${toString()} in four wheel drive")
        } else {
            super.drive()
        }
    }
}

In this class, we are modifing the already existant behaviors of driving and going in reverse. If the truck has four wheel drived turned on, the output of the program reflects this fact. One the other hand, if four wheel drive is turned off, the methods call super.drive(), which means use the behavior defined in Truck (which bubbles up to the original behavior in Vehicle). Thus, FourWheelDrive has specialized behaviors that were originally found in Vehicle. This is known as overriding behaviors.

Now let’s do a demonstration

fun main(args : Array<String>){
    val car = Vehicle("Fiat", "500", 2012)
    val truck = Truck("Chevy", "Silverado", 2017, 8000)
    val fourWheelDrive = FourWheelDrive("Dodge", "Ram", 2017, 8000)

    //drive() comes from Vehicle
    car.drive()
    println()

    //There is no drive() in Truck, but it 
    //inherited the behavior from Vehicle
    truck.drive()
    println()

    //FourWheelDrive override drive() to customize it
    fourWheelDrive.drive()
    println()

    println("Turn on four wheel drive")
    fourWheelDrive.fourByFour = true
    fourWheelDrive.drive()
}

Output

Driving 2012 Fiat 500

Driving 2017 Chevy Silverado

Driving 2017 Dodge Ram

Turn on four wheel drive
Driving 2017 Dodge Ram in four wheel drive

Three vehicles are made at the beginning of main: car, truck, and fourWheelDrive. They are all of type Vehicle, but Truck is a specialized case of Vehicle and fourWheelDrive is a specialized case of Truck. As such, all three objects have a drive() method which we use in the example program. When fourWheelDrive turns on fourByFour and then invokes drive, the console prints out that it is driving in four wheel drive.

Sealed Class

Although Kotlin supports inheritance, it’s use is discouraged. In order to use inheritance in Kotlin, the ‘open’ keyword needs to be added in front of the ‘class’ keyword first. If the ‘open’ keyword is ommitted, the class is considered to be final and the compiler will not allow the class to be used as a parent class. Likewise, all functions in a class also have to be marked as ‘open’ (see drive and reverse in vehicle), otherwise, overriding behaviors is not permitted.

Why would Kotlin choose to do this while other languages encourage inheritence? After studying issues found with inheritance, it became clear that many developers wrote classes without considering that a class may be extended later on. When changes where made in the parent class, the child classes could potentially break as well. This ended up creating a situation called “fragile base classes”.

Kotlin designers decided that by making developers mark classes as open, it would encourage developers to think about the needs of child classes when working on a base class. Kotlin also has powerful delegation mechanisms that encourage developers to use composition and delegation as code reuse mechanisms rather than inheritance.

Example program

Here is the entire source code used in this post

//Has to be marked as open to allow inheritance
open class Vehicle(
        private val make : String,
        private val model : String,
        private val year : Int) {

    fun start() = println("Starting up the motor")

    fun stop() = println("Turning off the engine")

    fun park() = println("Parking " + toString())

    //Has to be marked as open to allow overriding
    open fun drive() = println("Driving " + toString())

    //Has to be marked as open to allow overriding
    open fun reverse() = println("Reversing " + toString())

    override fun toString(): String {
        return "${year} ${make} ${model}"
    }
}

//Has to be marked as open for inheritance
open class Truck(make: String,
            model: String,
            year: Int,
            private val towCapacity : Int) : Vehicle(make, model, year) {
    fun tow () = println("${toString()} is towing ${this.towCapacity} lbs")
}

//This class is not open and therefore cannot be inherited from
class FourWheelDrive(make: String, model: String, year: Int, towCapacity: Int) :
        Truck(make, model, year, towCapacity) {

    var fourByFour = false

    //The override keyword signals to the compiler that we are overriding
    //the drive() method
    override fun drive() {
        if(fourByFour){
            println("Driving ${toString()} in four wheel drive")
        } else {
            super.drive()
        }
    }

    //The override keyword signals to the compiler that we are overriding
    //the reverse() method
    override fun reverse() {
        if(fourByFour){
            println("Reversing ${toString()} in four wheel drive")
        } else {
            super.drive()
        }
    }
}

fun main(args : Array<String>){
    val car = Vehicle("Fiat", "500", 2012)
    val truck = Truck("Chevy", "Silverado", 2017, 8000)
    val fourWheelDrive = FourWheelDrive("Dodge", "Ram", 2017, 8000)

    //drive() comes from Vehicle
    car.drive()
    println()

    //There is no drive() in Truck, but it
    //inherited the behavior from Vehicle
    truck.drive()
    println()

    //FourWheelDrive override drive() to customize it
    fourWheelDrive.drive()
    println()

    println("Turn on four wheel drive")
    fourWheelDrive.fourByFour = true
    fourWheelDrive.drive()
}

Kotlin Encapsulation and Procedural Programming

Software developers use the term Encapsulation to refer to grouping related data and behavior into a single unit, usually called a class. The class can be seen as the polar opposite of procedural based programming where data and behavior are treated as two distinct concerns. It should be noted that OOP and procedural programming have their distinct advantages and one should not be thought of as better as the other. Kotlin supports both styles of programming and it’s not uncommon to see a mix of both procedural and OOP programming.

Procedural Programming Example

Let’s with an example of procedural programming. In this example, we are working with a rectangle object. Its data is stored in a map (a data structure that supports key-value pairs) and then we have functions that consume the data.

val rectangle = mutableMapOf("Width" to 10, "Height" to 10, "Color" to "Red")

fun calcArea(shape : Map<String, Any>) : Int {
    return shape["Height"] as Int * shape["Width"] as Int
}

fun toString(shape : Map<String, Any>) : String {
    return "Width = ${shape["Width"]}, Height = ${shape["Height"]}, Color = ${shape["Color"]}, Area = ${calcArea(shape)}"
}

So we begin with the rectangle object that holds some properties of our rectangle: width, height, and color. Two functions follow the creation of the rectangle. They are calcArea and toString. Notice that these are global functions that accept any Map. This is dangerous because we can’t guarantee that the map will have “Width”, “Height”, or “Color” keys. Another issue is our loss of type safety. Since we need to store both Integers and Strings in the rectangle map, our value has to be of type Any, which is the base type in Kotlin.

OOP

Here is the same problem solved with an OOP approach.

class Rectangle(
    var width : Int,
    var height : Int,
    var color : String){

    fun calcArea() = this.width * this.height

    override fun toString() =
            "Width = ${this.width}, Height = ${this.height}, Color = ${this.color}, Area = ${calcArea()}"
}

The OOP solution demonstrates encapsulation because the data and the behavior associated with the data are grouped into a single entity called a class. The data associated with a class are often referred to as “properties” while the behaviors defined in the class are usually called “methods”. The calcArea() and toString() methods are always guaranteed to work because all objects based on the Rectangle class always have width, height, and color. We also do not lose our type safety because we are free to declare each property as a distinct variable within the class along with it’s type.

When the calcArea() and toString() methods are used, the word ‘this’ refers to the object that is calling these methods. You will notice that unlike the procedural program above, there is no Rectangle parameter supplied to calcArea() or toString(). Instead, the ‘this’ keyword is updated to refer to the object that is currently in use.

Tips on how to choose between Procedural and OOP

It should be noted that many software projects mix procedural and OOP programming. It’s also worth mentioning that almost anything that can be done with OOP can most likely be accomplished with procedural program and vice versa. However, some problems can be more easily solved when using procedural rather than OOP and other problems are better solved with OOP.

Procedural

  • Pure functional program: When we work in terms of pure mathematical functions where a function accepts certain inputs and returns certain outputs without side effects
  • Multi-threading: Procedural programming can help solve many challenges found in multi-threading environments. The integrity of mutable data is always concern in multi-threading, so functional programming works well provided the functions are pure functions that do not change data
  • Input and Output: In many cases, using a class to persist or retrieve an object from a data store is overkill. The same is true for printing to standard IO. Java has been heavily criticized for using the System.out.println() to write text to the console. Kotlin simplified this to println()

OOP and Encapsulation

  • GUI Toolkits: Objects representing buttons, windows, web pages, etc are very well modeled as classes
  • Grouping state or behavior: We often find that entities in software have properties or methods that are commonly held by other similar entities. For example, all road vehicles have wheels and move. Trucks are a specialized vehicle that has a box. Four-wheel drive trucks are specialized trucks that have four-wheel drive. We can use OOP to group all of the items common to all vehicles in a Vehicle class. All items common to Trucks can go in a Truck class, and finally, all items used only in four-wheel drive trucks can go in FourByFourTruck
  • Modularization: OOP allows developers to modularize code into smaller and reusable software components. Since the units of code are small pieces in a system, the code is usually easier to maintain.

Putting it together

Below is a working program that demonstrates both procedural programming and OOP.

package ch1

/**
 * This is a shape object without OOP. Notice how the data is separated from the behavior that works on the
 * data. The data is stored in a Map object, which uses key-value pairs. Then we have separate functions that
 * manipulate the data.
 */
val rectangle = mutableMapOf("Width" to 10, "Height" to 10, "Color" to "Red")

fun calcArea(shape : Map<String, Any>) : Int {
    //How can we guarantee that this map object has "Height" and "Width" property?
    return shape["Height"] as Int * shape["Width"] as Int
}

fun toString(shape : Map<String, Any>) : String {
    return "Width = ${shape["Width"]}, Height = ${shape["Height"]}, Color = ${shape["Color"]}, Area = ${calcArea(shape)}"
}

/**
 * This is a class that represents a Rectangle. You will immediately notice it has less code that the
 * non-OOP implementation. That's because the state (width, height, and color) are grouped together with
 * the behavior. Kotlin takes this a step further by providing us with behavior that lets us change
 * width, height, and color. We only need to add calcArea(), which we can guarantee will always work because
 * we know that there will always be width and height. Likewise, we know our toString() will never fail us
 * for the same reason!
 */
class Rectangle(
    var width : Int,
    var height : Int,
    var color : String){

    fun calcArea() = this.width * this.height

    override fun toString() =
            "Width = ${this.width}, Height = ${this.height}, Color = ${this.color}, Area = ${calcArea()}"
}

fun main(args : Array<String>){
    println("Using procedural programming")
    println(toString(rectangle))

    println("Changing width")
    rectangle["Width"] = 15
    println(toString(rectangle))

    println("Changing height")
    rectangle["Height"] = 80
    println(toString(rectangle))

    println("Changing color")
    rectangle["Color"] = "Blue"
    println(toString(rectangle))

    println("\n*****************************\n")
    println("Now using OOP")

    val square = Rectangle(10, 10, "Red")
    println(square)

    println("Changing height")
    square.height = 90
    println(square)

    println("Changing width")
    square.width = 40
    println(square)

    println("Changing color")
    square.color = "Blue"
    println(square)
}

Here is the output

Using procedural programming
Width = 10, Height = 10, Color = Red, Area = 100
Changing width
Width = 15, Height = 10, Color = Red, Area = 150
Changing height
Width = 15, Height = 80, Color = Red, Area = 1200
Changing color
Width = 15, Height = 80, Color = Blue, Area = 1200

*****************************

Now using OOP
Width = 10, Height = 10, Color = Red, Area = 100
Changing height
Width = 10, Height = 90, Color = Red, Area = 900
Changing width
Width = 40, Height = 90, Color = Red, Area = 3600
Changing color
Width = 40, Height = 90, Color = Blue, Area = 3600