Tuesday, October 15, 2019

Intro to Kotlin, Part 1 Outline

So let's start where we always start, with a hello world application:
fun main() {
  println("Hello world!")
}
A couple of things to note here:
  • Just like in Java, the entry point into the code is with the main method
  • But unlike Java, the main method doesn't need to be in a class
  • It's also not necessary to specify the args parameter, though you can if you need it, like this:
fun main(args: Array<String>) {
  println("Hello world!")
}
Some other important points to note:
  • Functions are defined using the fun keyword
  • Parameters are defined by first specifying the name, followed by a colon and the type
Other important points for functions:
  • You specify the return type using a colon and a type at the end of the function
fun main() {
  println(greeting())
}

fun greeting(): String {
  return "Hello world!"
}
  • For functions that are a single line, you can also use expression bodies
fun main() {
  println(greeting())
}

fun greeting(): String =
  "Hello world!"
  • And for functions that do use an expression body, you can let it infer the return type
fun main() {
  println(greeting())
}

fun greeting() =
  "Hello world!"
  • You can also create parameters with default values
fun main() {
  println(greeting())
}

fun greeting(
  greeting: String = "Hello",
  name: String = "world"
) = "$greeting $name!"
  • And you can choose which parameters you want to provide via named arguments
fun main() {
  println(greeting(name = "Bob"))
}

fun greeting(
  greeting: String = "Hello",
  name: String = "world"
) = "$greeting $name!"
  • You can have a vararg parameter using the vararg keyword
fun main() {
  println(greeting("Hi", "Bob", "Sue"))
}

fun greeting(
  greeting: String = "Hello",
  vararg names: String = arrayOf("world")
) = "$greeting ${names.joinToString(" and ")}!"
  • Also if you want to pass an array into the vararg parameter, you need to explicitly use the spread operator
fun main() {
  val names = arrayOf("Bob", "Sue")
  println(greeting("Hi", *names))
}

fun greeting(
  greeting: String = "Hello",
  vararg names: String = arrayOf("world")
) = "$greeting ${names.joinToString(" and ")}!"
So with that, let's move on to variables:
  • The difference between val and var is that val can only be assigned to once, whereas var allows reassignment
fun main() {
  val myVal = "Test 1"
  myVal = "Test 2" //This line breaks
  
  var myVar = "Test 1"
  myVar = "Test 2" //This works fine
}
  • Kotlin is a statically typed language, but it is able to infer the type if it's being immediately assigned a value
  • If the value isn't immediately specified, then a type must be specified at declaration
fun main() {
  val myVal: String
  myVal = "Test 1"
}
  • Also note that the type cannot be changed
fun main() {
  var myVar = "Test 1"
  myVar = 2 //This is not allowed
}
  • All types in Kotlin come in nullable and non null variants
  • Non null types cannot be null
fun main() {
  val myVal: String = null // This is not allowed
}
  • You make a type nullable by adding a ?
fun main() {
  val myVal: String? = null // This works fine
}
  • You can't do anything with a nullable type until you've null checked it
fun main() {
  val greeting: String? = "Hello world!"
  greeting.substring(1, 3) // This doesn't compile
  if (greeting != null) {
    greeting.substring(1, 3) //This works fine
  }
}
  • There are operators that help to make null checks easier, such as the safe call operator
fun main() {
  val greeting: String? = "Hello world!"
  greeting?.substring(1, 3)
}
  • And the elvis operator
fun main() {
  val greeting: String? = "Hello world!"
  (greeting ?: "Some default").substring(1, 3)
}
Strings
  • You can insert a variable into a string template using the $ followed by the variable name
fun main() {
  println(greeting(name = "Bob"))
}

fun greeting(
  greeting: String = "Hello",
  name: String = "world"
) = "$greeting $name!"
  • And you can insert logic using the $ by sticking it inside {}
fun main() {
  println(greeting("Hi", "Bob", "Sue"))
}

fun greeting(
  greeting: String = "Hello",
  vararg names: String = arrayOf("world")
) = "$greeting ${names.joinToString(" and ")}!"
  • We can also create multi-line strings
fun main() {
  val insert = "Some insert"
  val str1 = """
    |I'm
    |  a multi-line
    |    string
    |      with indentation and and and insert: $insert
  """.trimMargin()
  println(str1)
}
If statements
  • The major difference between if statements in Java and Kotlin is that in Kotlin an if statement can return a value
fun main() {
  val result = if (1 > 2) {
    "Not happening"
  } else {
    "Here's the result"
  }
  println(result)
}
When statements
  • When statements are essentially a much more powerful switch statement
fun main() {
  val myVal: Any = "My Test"
  when (myVal) {
    is String -> println(myVal.substring(3))
    is Int -> println(myVal + 5)
    else -> println("Unknown")
  }

  val myOtherVal = "Test 2"
  val result = when (myOtherVal) {
    "Test 1" -> "Result 1"
    "Test 2" -> "Result 2"
    else -> "Other"
  }
  println(result)

  val result2 = when {
    myOtherVal == myVal -> "Equal"
    myOtherVal.startsWith("Test") -> "Test"
    else -> "Other"
  }
  println(result2)
}
While loops
  • While and do-while loops function the same way as they do in Java
For each loops
  • For loops don't exist in Kotlin, you instead do everything with for each loops
fun main() {
  for (i in 1..5) {
    print("$i ")
  }
  println()

  for (i in 1 until 5) {
    print("$i ")
  }
  println()

  for (i in 5 downTo 1) {
    print("$i ")
  }
  println()

  for (i in 1..5 step 2) {
    print("$i ")
  }
  println()
  println()

  val list = listOf("One", "Two", "Three")
  for (i in 0 until list.size) {
    println("$i: ${list[i]}")
  }
  println()

  for (i in list.indices) {
    println("$i: ${list[i]}")
  }
  println()

  for (item in list) {
    println(item)
  }
  println()
}
Object destructuring
  • You can use object destructuring to directly access the properties on a object
fun main() {
  val map = mapOf("key1" to "value1", "key2" to "value2")

  for (pair in map) {
    println("${pair.key} ${pair.value}")
  }
  println()

  for ((key, value) in map) {
    println("$key, $value")
  }
  println()
}
  • You can also use the underscore to omit properties that you don't care about
fun main() {
  val map = mapOf("key1" to "value1", "key2" to "value2")

  for ((key, _) in map) {
    println(key)
  }
  println()
}
Equality
  • The == in Kotlin is equivalent to the .equals call in Java
  • If you need Java's == functionality, in Kotlin you use ===
Try, catch, finally
  • This works similar to how it works in Java, but you can return a value from it
fun main() {
  val myVal = "Hello world"
  val result = try {
    myVal.toInt()
  } catch (e: Exception) {
    0
  }
  println(result)
}