It’s an interesting language that tries to prevent programmers from shooting themselves in the foot while trying to maintain the efficiency of the JVM. Here are some interesting and sometimes (pleasantly) surprising things Kotlin does.
Null needs to be explicity defined
if vs when
Both are expressions that can return values.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
funmain() { print("Enter the temperature:")
val input = readLine() if (input == null) return
val comment = if (input.toInt() > 37) { println("seems hot...") "Make an omlette on the pavement" } elseif (input.toInt() < 0) { "Brrr...that's cold" } else { "Nice day for a walk" } println(comment) }
1 2 3 4 5 6 7 8 9 10 11
funmain() { print("Enter the temperature:") val input = readLine() val comment = when(input?.toInt()) { in-40..5 -> "Brrr...that's cold" in6..24 -> "Mmmm...that's crisp" in25..37 -> "Getting hot in here" else -> "Make an omlette on the pavement" } println(comment) }
arrays
You can create arrays of mixed types
1 2 3 4 5 6
funmain() { var collection = arrayOf("Apple", 23.6, 'D', 77) for (e in collection) { println(e) } }
If we convert to bytecodes and look at the Java equivalent we see it’s just an array of Objects.
funmain() { outer@for (i in"This is the outer loop") { for (j in"This is the inner loop") { if (j in"aeiou") break@outer println("$i .. $j") } } }
Functions
It’s cool that functions can be defined as expressions
1
funproduct(a: Int, b: Int) = a * b
Smart Casts
Safe casts are inserted when needed.
1 2 3 4 5 6 7
val a :Any a = "45"
if (a !is String) return println("Len = ${a.length}") println("this prints too") println("and so does this")
The lines after the condition are only executed for Strings.
Properties
Classes can have properties which can be marked as read-only(val) or read-write(var). Variables be initialized to be used. If it cannot be initialized then qualify it with lateinit.
Constructors
There are primary and secondary constructors.
Primary
1 2 3 4 5 6
classDog(val name :String)
funmain() { var d = Dog("Spike") println("My name is ${d.name}") }
Secondary
1 2 3 4 5 6 7 8 9 10 11
classDog{ val name: String constructor(name: String) { this.name = name } }
funmain() { var d = Dog("Spike") println("My name is ${d.name}") }
Data classes
Inheritance
Modifiers are explicit at the function level. By default functions are marked as closed and need to be qualified with open to be able to override it.
var <propertyName>[: <PropertyType>] [= <property_initializer>] [<getter>] [<setter>]
Abstract classes
Abstract classes allow us to define specific traits similar to interfaces.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
abstract class Plane(val name: String) { abstract fun fly() }
fun main() { val p = F120("Zippy") println(p) }
class F120(myname: String): Plane(myname) { override fun fly() { println("zooooom") } override fun toString(): String { return "name --> $name" } }
Functional Programming [TODO]
One of the paradigms that can be used.
1 2
Coroutines
Basic example
1 2 3 4 5 6 7 8 9 10
import kotlinx.coroutines.*
funmain() { GlobalScope.launch { // launch new coroutine in background and continue delay(3333L) // non-blocking delay for 1 second (default time unit is ms) println("${Thread.currentThread()} World!") // print after delay } println("${Thread.currentThread()} Hello,") // main thread continues while coroutine is delayed Thread.sleep(5000L) // block main thread for 2 seconds to keep JVM alive }
funreverse(list: List<Int>): List<Int> { val result = arrayListOf<Int>() for (i in0..list.size - 1) { result.add(list[list.size - 1 - i]) } return result }
dataclassResult(val failed: Boolean, val numTries: Int)
funmain(args: Array<String>) { print("What word do you want to guess? ") val word = readLine() if (word == null) { println("No word provided...exiting") return }
for (i in1..100) println()
val letters = word.toLowerCase().toCharArray().toHashSet() val correctGuesses = mutableSetOf<Char>() var totalTries = 0
while (letters != correctGuesses) { print("Enter letter: ") val c = readLine() if (c == null) { println("Bad input") return } val (failed , numTries) = guessWord(c.first(), correctGuesses, totalTries, word) totalTries = numTries if (!failed) break } println("Number of wrong guesses = $totalTries"); }
funguessWord(char: Char, correctGuesses: MutableSet<Char>, fails: Int, word: String): Result { var failed = false var count = fails //if (correctGuesses.contains(char)) return Result(false, fails) for(c in word) { if (c == char) { print("$c ") correctGuesses.add(c) } elseif (correctGuesses.contains(c)) { print("$c ") } else { print("_ ") failed = true } } if (failed) count++ return Result(failed, count) }
Example: Get Max IP address count from file
1 2 3 4 5 6 7 8 9 10 11 12 13 14
package collections
import java.io.File
funmain(args: Array<String>) { var file = File("input.txt") val addrCount = mutableMapOf<String, Int>() var maxPair: Pair<String, Int> = Pair("", 0) file.forEachLine { addrCount.put(it, addrCount.getOrDefault(it, 0) + 1) maxPair = if (maxPair.second < addrCount.get(it)!!) Pair(it, addrCount[it]!!) else maxPair } println(maxPair) }
Example: Capitalize Words in a Sentence
Implementation
1 2 3 4 5 6
package capitalizeSentence
funcapitalizeLetter(sentence: String) : String { val s = sentence.split(" ").joinToString(separator = " ") { it.capitalize()} return s }
@Test fun `range 5-7 contains 5-7`() { assertTrue(checkSubrange(5..7, 5..7)) }
@Test fun `range 6-7 does not contain 5-7`() { assertFalse(checkSubrange(6..7, 5..7)) }
@Test fun `range 5-8 does not contain 3-5`() { assertFalse(checkSubrange(5..8, 3..5)) } }
Example: Add number up to N
Given positive integer n implement a function which calculates sum of all numbers from 1 up to (and including) number n.
Implementations
1 2 3 4 5 6 7 8 9 10
import java.lang.IllegalArgumentException
funaddUpTo(p: Int): Int { if (p < 0) throw IllegalArgumentException("Cannot solve for negatives!") var amount = 0 for (e in0..p) { amount += e } return amount }
More idiomatic solutions
1 2 3 4 5 6 7 8 9
funaddUpTo2(p: Int): Int { if (p < 0) throw IllegalArgumentException("Cannot solve for negatives!") return (1..p).sum() }
funaddUpTo3(p: Int): Int { if (p < 0) throw IllegalArgumentException("Cannot solve for negatives!") return (0..p).fold(0) {accumulated, current -> accumulated + current} }