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

Function Overloading

Kotlin supports function overloading, which is a form of polymorphism where the compiler deduces which function should get called based on the type and position of the arguments. When we overload functions, we create functions that have the same name, but different return types or arguments. This allows us to write code that is customized based on the type of the parameters based on the function. Let’s begin with an example.

class Bob(val name : String = "Bob")

/**
 * Here are five functions with the same name. Each function is resolved
 * based on the type of parameters that are supplied.
 */
fun foo(name : String) {
    println("Inside foo(String)")
    println(name)
}

fun foo(num : Int){
    println("Inside foo(Int)")
    println(num)
}

fun foo(bob : Bob){
    println("Inside of foo(Bob)")
    println(bob.name)
}

fun foo(any : Any){
    println("Inside of foo(Any)")
    println(any)
}

fun foo(name : String, any : Any){
    println("Inside of foo(name, any)")
    println("$name, $any")
}

Here are five functions named foo. The compiler can figure out which function to use because all five of our foo functions take a different type as an argument. So when we call foo(“Hello World”) the version of foo that takes a String parameter is used because “Hello World” is a String. Likewise, when we call foo(12), the compiler will choose the foo(Int) version because 12 is an int. The final version of foo takes two parameters, String and Any. It will get called when we supply a String and another object so foo(“Hello”, “Bob Belcher”) matches to foo(String, Any) as well as foo(“Bob is #”, 1).

Optional Arguments

The designers of Kotlin realized that while function overloading is useful, it can also turn into verbose code. Kotlin supports optional arguments and named arguments to help address the problem.

fun foo(num : Long = 0,
        name : String = "",
        bob : Bob = Bob(),
        any : Any = object{
    override fun toString() = "Object"
}){
    println("Inside of default argument function")
    println(num)
    println(name)
    println(bob)
    println(any)
}

When this code compiles, the compiler will basically overload the foo function for us, but we can avoid writing a bunch of overloaded function when we use optional arguments correctly. This version of foo can be called by writing foo(), which uses all of the defaults, foo(num = 12L), which uses the defaults except for the num parameter which is set to 12. We can also write foo(any = Bob()), and so on.

Putting it Together

Here is a sample driver program that shows off function overloading and optional arguments.

package ch1.overloading

class Bob(val name : String = "Bob")

/**
 * Here are five functions with the same name. Each function is resolved
 * based on the type of parameters that are supplied.
 */
fun foo(name : String) {
    println("Inside foo(String)")
    println(name)
}

fun foo(num : Int){
    println("Inside foo(Int)")
    println(num)
}

fun foo(bob : Bob){
    println("Inside of foo(Bob)")
    println(bob.name)
}

fun foo(any : Any){
    println("Inside of foo(Any)")
    println(any)
}

fun foo(name : String, any : Any){
    println("Inside of foo(name, any)")
    println("$name, $any")
}

//Note that we can also use default arguments in Kotlin
//which lets us reduce function overloading
fun foo(num : Long = 0,
        name : String = "",
        bob : Bob = Bob(),
        any : Any = object{
    override fun toString() = "Object"
}){
    println("Inside of default argument function")
    println(num)
    println(name)
    println(bob)
    println(any)
}

fun main (args : Array<String>){
    //Resolves to foo(String) because "Hello World" is a String
    foo("Hello World")

    //Resolves to foo(Int) because 1 is an int
    foo(1)

    //Resolve to foo(Bob) because Bob is Bob
    foo(Bob())

    //Resolves to foo(Any) because the object expression matches
    //to Any, which is the base type of all objects in Kotlin
    foo(object { override fun toString() = "Object"})
    
    //Matches to foo(String, Any)
    foo("Hello ", Bob())

    foo() //Called with optional arguments.

    foo(num = 2L) //Called with only using one of the optional arguments
}

When run, we get this output

Inside foo(String)
Hello World
Inside foo(Int)
1
Inside of foo(Bob)
Bob
Inside of foo(Any)
Object
Inside of foo(name, any)
Hello , ch1.overloading.Bob@3caeaf62
Inside of default argument function
0

ch1.overloading.Bob@6a38e57f
Object
Inside of default argument function
2

ch1.overloading.Bob@5577140b
Object

Kotlin Access Modifiers

Encapsulation is a huge part of OOP. Hiding method and behavior is an important part in achieving encapsulation so Kotlin provides us with access modifiers to help in this effort.

Kotlin has four kinds of access modifiers

  • private—Items marked as private are visible only to the class
  • protected-Items marked as protected are visible to the class and it’s child classes
  • internal—Items marked as internal are accessible to all members of the Kotlin module but are not available outside of the module
  • public—Items marked as public are available to the module and outside of the module using the import statement
    1. Let’s walk through some examples.

      Public

      Public is the default visiblity in Kotlin. If the public keyword is omitted, the member will be marked as public.

      fun printBurgerOfTheDay(burgerName : String = "Never been Feta")
              = println(burgerName)
      

      The printBurgerOfTheDay function is accessible to all code within the module that contains it, plus any module that imports the printBurgerOfTheDay function.

      Internal

      Members marked as internal are visible to the module but are not accessible outside of the module. In other words, internal members act like public members, but they can’t be imported into other modules.

      internal var burgerName = "Mission A-Corn-Plished Burger"
      

      The above variable can be accessed in the module. It may not be imported into another module.

      Protected

      While public and internal access can be used on both class and non-class members, protected only applies to classes.

      open class Cook {
          protected val position = "Cook"
          private val name = "No Name"
      
          override fun toString(): String {
              return position + ", " + name
          }
      }
      
      class Bob : Cook() {
      
          fun printPosition() = println(position)
      }
      

      In the above example, we have a Cook and a Bob class. The Cook class has a protected property called position. The position property is accessible to Cook but it’s also accessible to Bob because Bob is a child class of Cook. In the above example, Bob has a printPosition() function that uses the position property.

      Private

      Private access is the most restrictive. When a member is marked as private, it is only accessible to the class. In the above code snippet, Cook also has a name property, but the name property is not accessible to Bob because it is private.

      Putting it all together

      Below is an example program that shows all of the possible access modifiers in question.

      package ch1.accessmodifiers
      
      /**
       * When no access modifier is used, public is used by default.
       * This printBurgerOfTheDay function is visible to the entire program
       */
      fun printBurgerOfTheDay(burgerName : String = "Never been Feta")
              = println(burgerName)
      
      /**
       * This extension function is marked private. It can't even be used in this module
       */
      private fun String.makeBurgerOfTheDay(burgerName : String) : String = burgerName
      
      /**
       * This variable is marked as internal. It's visible throughout the module,
       * but it can't be accessed outside of the module
       */
      internal var burgerName = "Mission A-Corn-Plished Burger"
      
      /**
       * The position property on Cook is marked as protected. It is only accessible
       * to the Cook class an it's child classes. The name property is private and may
       * only be used by Cook.
       */
      open class Cook {
          protected val position = "Cook"
          private val name = "No Name"
      
          override fun toString(): String {
              return position + ", " + name
          }
      }
      
      class Bob : Cook() {
      
          fun printPosition() = println(position)
      }
      
      fun main(args : Array<String>){
          //Using the public function printBurgerOfTheDay
          printBurgerOfTheDay()
      
          //Printing the internal burgerName variable
          printBurgerOfTheDay(burgerName)
      
          //Create an instance of Cook
          val cook = Cook()
          println(cook)
      
          //Create an instance of Bob
          val bob = Bob()
          bob.printPosition()
      
          //try and use the String extension function
          //DOESN'T COMPILE because makeBurgerOfTheDay is private
          //burgerName = String.makeBurgerOfTheDay("Rest in Peas Burger")
      }
      

      Here is the output when run

      Never been Feta
      Mission A-Corn-Plished Burger
      Cook, No Name
      Cook
      

      References

      https://kotlinlang.org/docs/reference/visibility-modifiers.html

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)))
}