Kotlin Tutorial

Kotlin TutorialKotlin is a fantastic language that improves greatly upon Java and at the same time can use all Java libraries and frameworks. It provides both the power of OOP and functional programming. It is quickly becoming the language of choice for developing Android apps. In this video I will cover what you’d learn about the core syntax of Kotlin in a 300 page book all in one video.

I’ll cover Installation for MacOS and Windows, Variables, Casting, Strings, Arrays, Ranges, Conditionals, Loops, Functions, Higher Order Functions, Collection operators, Exception Handling, Lists, Maps, Classes, Inheritance, Interfaces, Null Safety and so much more.

If you value tutorials like this consider donating a $1 on Patreon.

[googleplusone]

Code & Cheat Sheet

/**
 * Created by derekbanas
 */

package demo

import java.util.Random

fun main(args : Array<String>) {
    println("Hello, world!")

    // ----- VARIABLES -----

    // Create a read only variable
    val name = "Derek"

    // Mutable (changeable) variable
    var myAge = 42

    // Kotlin uses type inference, but you can define the type

    var bigInt: Int = Int.MAX_VALUE
    var smallInt: Int = Int.MIN_VALUE

    println("Biggest Int : " + bigInt)
    println("Smallest Int : " + smallInt)

    var bigLong: Long = Long.MAX_VALUE
    var smallLong: Long = Long.MIN_VALUE

    println("Biggest Long : " + bigLong)
    println("Smallest Long : " + smallLong)

    var bigDouble: Double = Double.MAX_VALUE
    var smallDouble: Double = Double.MIN_VALUE

    println("Biggest Double : " + bigDouble)
    println("Smallest Double : " + smallDouble)

    var bigFloat: Float = Float.MAX_VALUE
    var smallFloat: Float = Float.MIN_VALUE

    println("Biggest Float : " + bigFloat)
    println("Smallest Float : " + smallFloat)

    // Doubles are normally precise to 15 digits
    var dblNum1: Double = 1.11111111111111111
    var dblNum2: Double = 1.11111111111111111

    println("Sum : " + (dblNum1 + dblNum2))

    /* We also have the following
    Short 16 Bytes
    Byte 8 Bytes
    */

    // Booleans are either true or false
    if (true is Boolean){
        print("true is boolean\n")
    }

    // Characters are single quoted characters
    var letterGrade: Char = 'A'

    println("A is a Char : " + (letterGrade is Char))

    // ----- CASTING -----
    // You can cast from one type to another using
    // toShort, toInt, toLong, toFloat, toDouble, toChar,
    // toString

    println("3.14 to Int : " + (3.14.toInt()))
    println("A to Int : " + (letterGrade.toInt()))
    println("65 to Char : " + (65.toChar()))

    // ----- STRINGS -----
    // Strings are double quoted series of characters

    val myName = "Derek Banas"

    val longStr = """This is a
    long string """

    var fName = "Doug"
    var lName = "Smith"

    // You can change values
    fName = "Sally"

    // You can combine strings
    var fullName = fName + " " + lName

    // You can use string interpolation
    println("Name : $fullName")

    // You can perform other operations with {}
    println("1 + 2 = ${1 + 2}")

    // Get length
    println("String length : ${longStr.length}")

    var str1 = "A random string"
    var str2 = "a random string"

    // Compare strings
    println("Strings Equal : ${str1.equals(str2)}")

    // Compare strings
    // 0 : Equal, Negative if less, Positive if greater
    println("Compare A to B : ${"A".compareTo("B")}")

    // Get character at an index
    println("2nd Index : ${str1.get(2)}")

    // Get a substring from start up to but not including end
    println("Index 2-7 : ${str1.subSequence(2,8)}")

    // Checks if a string contains another
    println("Contains random : ${str1.contains("random")}")


    // ----- ARRAYS -----
    // You can store multiple types in arrays
    var myArray = arrayOf(1, 1.23, "Doug")

    // You can access values using indexes starting at 0
    println(myArray[2])

    // Change the value
    myArray[1] = 3.14
    println(myArray[1])

    // Elements in array
    println("Array Length : ${myArray.size}")

    // Is element in the array
    println("Doug in Array : ${myArray.contains("Doug")}")

    // Get first 2 elements in array as an array
    var partArray = myArray.copyOfRange(0,1)

    // Get the first element
    println("First : ${str1.first()}")

    // Get index of value
    println("Doug Index : ${str1.indexOf("Doug")}")

    // Create an array of squares
    var sqArray = Array(5, { x -> x * x})
    println(sqArray[2])

    // There are type specific arrays
    var arr2: Array<Int> = arrayOf(1,2,3)
    println(arr2[2])


    // ----- RANGES -----
    // You define ranges by providing a starting and ending
    // value

    val oneTo10 = 1..10
    val alpha = "A".."Z"

    // Use in to search a Range
    println("R in alpha : ${"R" in alpha}")

    // Create ranges that decrement
    val tenTo1 = 10.downTo(1)

    // Create array up to a value
    val twoTo20 = 2.rangeTo(20)

    // Step through an array while adding 3
    val rng3 = oneTo10.step(3)

    // Cycle through a range and print
    for(x in rng3) println("rng3 : $x")

    // Reverse a range
    for(x in tenTo1.reversed()) println("Reverse : $x")


    // ----- CONDITIONALS -----
    // Conditional Operators : >, <, >=, <=, ==, !=
    // Logical Operators : &&, ||, !

    val age = 8

    if (age < 5){
        println("Go to Preschool")
    } else if (age == 5){
        println("Go to Kindergarten")
    } else if ((age > 5) && (age <= 17)){
        val grade = age - 5
        println("Go to Grade $grade")
    } else {
        println("Go to College")
    }

    // When works like Switch in other languages
    when (age) {

        // Match a list
        0,1,2,3,4 -> println("Go to Preschool")

        // Match a specific value
        5 -> println("Go to Kindergarten")

        // Match a range
        in 6..17 -> {
            val grade = age - 5
            println("Go to Grade $grade")
        }

        // Default
        else -> println("Go to College")
    }


    // ----- LOOPING -----
    // You can use for loops to cycle through arrays
    // ranges, or anything else that implements the
    // iterator function

    for (x in 1..10){
        println("Loop : $x")
    }

    // Generate a random number from 1 to 50
    val rand = Random()
    val magicNum = rand.nextInt(50) + 1

    // While loops while a condition is true
    var guess = 0

    while(magicNum != guess){
        guess += 1
    }

    println("Magic num is $magicNum and you guessed $guess")

    for (x in 1..20){
        if (x % 2 == 0) {

            // Continue jumps back to the top of the loop
            continue
        }

        println("Odd : $x")

        // Break jumps out of the loop and stops looping
        if (x == 15) break

    }

    var arr3: Array<Int> = arrayOf(3,6,9)

    // Iterate for indexes
    for (i in arr3.indices){
        println("Mult 3 : ${arr3[i]}")
    }

    // Output indexes
    for ((index, value) in arr3.withIndex()){
        println("Index : $index & Value : $value")
    }


    // ----- FUNCTIONS -----
    // Functions start with fun, function name,
    // parameters and return type

    fun add(num1: Int, num2: Int) : Int = num1 + num2
    println("5 + 4 = ${add(5,4)}")

    // You don't need a return type with single line functions
    // You can define default values for parameters
    fun subtract(num1: Int = 1, num2: Int = 1) = num1 - num2
    println("5 - 4 = ${subtract(5,4)}")

    // You can use named parameters
    println("4 - 5 = ${subtract(num2 = 5, num1 = 4)}")

    // Use unit if you return nothing
    fun sayHello(name: String) : Unit = println("Hello $name")
    sayHello("Derek")

    // Functions can return 2 values with Pair and 3 with Triple
    val (two, three) = nextTwo(1)
    println("1 $two $three")

    // Send a variable number of parameters
    println("Sum : ${getSum(1,2,3,4,5)}")

    // We can define function literals
    val multiply = {num1: Int, num2: Int -> num1 * num2}
    println("5 * 3 = ${multiply(5,3)}")

    // Calculate the Factorial with Tail Recursion
    // Factorial 5 * 4 * 3 * 2 * 1
    println("5! = ${fact(5)}")

    // ----- HIGHER ORDER FUNCTIONS -----
    // Higher order functions either accepts or returns
    // another function

    // Use filter to find evens
    val numList = 1..20

    // If a function has only 1 parameter you don't
    // have to declare, but just use it instead
    val evenList = numList.filter { it % 2 == 0 }
    evenList.forEach { n -> println(n) }

    // Call a function that returns dynamically
    // created functions
    val mult3 = makeMathFunc(3)
    println("5 * 3 = ${mult3(5)}")

    // A function that receives a list and a function
    val multiply2 = {num1: Int -> num1 * 2}
    val numList2 = arrayOf(1,2,3,4,5)
    mathOnList(numList2, multiply2)

    // ----- COLLECTION OPERATORS -----

    // Use reduce to sum values in a list
    val listSum = numList2.reduce { x, y -> x + y }
    println("Reduce Sum : $listSum")

    // Fold is like Reduce, but it starts with an initial value
    val listSum2 = numList2.fold(5) { x, y -> x + y }
    println("Fold Sum : $listSum2")

    // Check if any values are even
    println("Evens : ${numList2.any {it % 2 == 0}}")

    // Check if all values are even
    println("Evens : ${numList2.all {it % 2 == 0}}")

    // Return a list of values greater then 3
    val big3 = numList2.filter { it > 3}
    big3.forEach { n -> println(">3 : $n") }

    // Use Map to perform an action on every item
    // and return a new list
    val times7 = numList2.map {it * 7}
    times7.forEach { n -> println("*7 : $n") }

    // ----- EXCEPTION HANDLING -----
    // Exceptions are handled just like with Java

    val divisor = 2

    try{
        if (divisor == 0){
            throw IllegalArgumentException("Can't Divide by Zero")
        } else {
            println("5 / $divisor = ${5/divisor}")
        }

    } catch (e: IllegalArgumentException){
        println("${e.message}")
    }

    // ----- LISTS -----
    // There are immutable Lists and mutable MutableLists

    // Create a mutable list
    var list1: MutableList<Int> = mutableListOf(1,2,3,4,5)

    // Create an immutable list
    val list2: List<Int> = listOf(1,2,3)

    // Add an item
    list1.add(6)

    // Get first item
    println("1st : ${list1.first()}")

    // Get last
    println("Last : ${list1.last()}")

    // Get value at index
    println("2nd : ${list1[2]}")

    // Get a list starting from index to another
    var list3 = list1.subList(0, 3)

    // Size of List
    println("Length : ${list1.size}")

    // Clear a Mutable list
    // list3.clear()

    // Remove a value
    list1.remove(1)

    // Remove at index
    list1.removeAt(1)

    // Add value at index
    list1[2] = 10


    list1.forEach { n -> println("Mutable List : $n") }


    // ----- MAPS -----
    // A modifiable collection that holds key value pairs

    // Create a Map
    val map = mutableMapOf<Int, Any?>()

    // Create a Map and add values
    val map2 = mutableMapOf(1 to "Doug", 2 to 25)

    // Add values
    map[1] = "Derek"
    map[2] = 42

    // Get Size
    println("Map Size : ${map.size}")

    // Add a key value
    map.put(3, "Pittsburgh")

    // Remove a key and value
    map.remove(2)

    // Iterate and get keys and values
    for((x, y) in map){
        println("Key : $x Value : $y")
    }

    // ----- CLASSES -----
    // Create an Animal object
    val bowser = Animal("Bowser", 20.0, 13.5)

    // Call method in the class
    bowser.getInfo()

    // ----- INHERITANCE -----
    // Create a class Dog that inherits from
    // the Animal class

    val spot = Dog("Spot", 20.0, 14.5, "Paul Smith")

    spot.getInfo()


    // ----- INTERFACES -----
    // Create a Bird object that implements the
    // Flyable interface

    val tweety = Bird("Tweety", true)

    tweety.fly(10.0)

    // ----- NULL SAFETY -----
    // Null safety is built into Kotlin

    // By default you cannot assign null
    // var nullVal: String = null

    // To allow for a null value use ?
    var nullVal: String? = null

    // A function that may return null uses ?
    // fun myFun(): String?

    // Kotlin provides for the opportunity of a
    // null value if an if statement protects
    // from danger
    fun returnNull(): String? {
        return null
    }

    var nullVal2 = returnNull()

    // This is a smart cast
    if(nullVal2 != null) {
        println(nullVal2.length)
    }

    // We could use the force operator !! to force
    // a null assignment
    var nullVal3 = nullVal2!!.length

    // The Elvis operator assigns a default value
    // if null
    var nullVal4: String = returnNull() ?: "No Name"

}

// ----- FUNCTIONS -----

// Returns 2 values
fun nextTwo(num: Int): Pair<Int, Int>{
    return Pair(num+1, num+2)
}

// Receive variable number of parameters
fun getSum(vararg nums: Int): Int{
    var sum = 0

    // For each value in the array add it to sum
    nums.forEach { n -> sum += n }

    return sum
}

fun fact(x: Int): Int {
    tailrec fun factTail(y: Int, z: Int): Int {
        if (y == 0) return z
        else return factTail(y - 1, y * z)
    }
    return factTail(x, 1)
}

// Returns a custom function that multiplies values
// times the value passed to it
fun makeMathFunc(num1: Int): (Int) -> Int = { num2 -> num1 * num2}

// Receives a list and a function to use on the list
fun mathOnList(numList: Array<Int>, myFunc: (num: Int) -> Int){
    for(num in numList){
        println("MathOnList : ${myFunc(num)}")
    }
}

// ----- CLASSES -----
// There are no static methods
// Classes are final by default unless marked open
// The fields must also be marked as open

open class Animal (val name: String, var height: Double, var weight: Double){

    // Objects are initialized in init
    init {

        // Regex that matches for a number any place
        // in a string
        val regex = Regex(".*\\d+.*")

        // If these requirements aren't met an
        // IllegalArgumentException is thrown
        require(!name.matches(regex)) {"Animal Name can't Contain Numbers"}

        require(height > 0) {"Height must be greater then 0"}

        require(weight > 0) {"Weight must be greater then 0"}

    }

    // If you want to allow overriding of this method
    // you must use open
    open fun getInfo(): Unit{
        println("$name is $height tall and weighs $weight")
    }
}

// ----- INHERITANCE -----
class Dog(name: String,
          height: Double,
          weight: Double,
          var owner: String) : Animal(name, height, weight){

    // Overriding Animal method
    override fun getInfo(): Unit{
        println("$name is $height tall, weighs $weight and is owned by $owner")
    }

}


// ----- INTERFACES -----
// An interface is a contract that states all fields
// and methods a class must implement

interface Flyable {
    var flies: Boolean

    fun fly(distMiles: Double): Unit
}

// We override flies in the constructor
// To implement the interface we follow the
// constructor parameters with a colon and the
// interface name
class Bird constructor(val name: String, override var flies: Boolean = true) : Flyable{

    // We must also override any methods in the interface
    override fun fly(distMiles: Double): Unit{
        if(flies){
            println("$name flies $distMiles miles")
        }
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *