Why Kotlin? An Introduction. (Part 4)

Back to the good: Control Flow

Why Kotlin? An Introduction. (Part 4)

Back to the good: Control Flow

We're going back to some of the good stuff right now. Control flow! Last week was a little more ranty, with a little less real content than I like, so I'm going to try to put a little more content in this week.

ZZZzzzzz….

We'll get the boring stuff out of the way first… What's the same in Kotlin as in Java? Most of it…

While and Do While still doing the same thing

Kotlin:

var someInt = 345
while (someInt < 1000) {
    someInt++
}
// someInt == 1000
do {
    someInt--
} while (someInt >= 1)
// someInt == 0

Java:

int someInt = 345;
while (someInt < 1000) {
    someInt++;
}
// someInt == 1000
do {
    someInt--;
} while (someInt >= 1);
// someInt == 0

Notice all I did was add some semi-colons and change how someInt was declared and we have the same results.

The subtle differences

While the syntax for if might look the same, if is actually an expression in Kotlin. You can write your if statements ALMOST (more coming) like you did in Java, but Kotlin adds a little more power.

val date = Date()

val calculation = if (date.time % 2 == 0L) {
    false
} else {
    3
}

See how we assign the value calculation here? If the current date().time is even, we assign it the value false otherwise we assign it the value of 3. I used this example for two reasons:

  1. This demonstrates the use of the last value of the if block as the value assigned to the if expression.
  2. This shows a weird capability of Kotlin whereas we can set the type of calculation somewhat arbitrarily.

Note the above, but I'd avoid drawing questions on the type of an object when working in production. The more assurance you have about your types the less edge cases you'll end up having to deal with and the easier your code will be to understand.

If else and switch cases

They don't exist! I was kind of surprised when I realized this, but then I realized how much I actually don't miss them.

  • Have you ever run into those lengthy, unreadable, jumbles of if-else style statements (especially true in Python)?
  • Have you ever been burnt because you forgot to break from a case?
  • Have you ever started a switch-case statement only realizing you had to use if-else because you value wasn't constant?

Well, good news! Kotlin has a solution… when. when is an expression which can be used to replace if-else and switch statements. Here's a simple example:

val logLevel = Level.FINEST

val levelString = when(logLevel) {
    Level.FINE -> "Fine"
    Level.FINER -> "Finer"
    Level.WARNING -> "Warning"
    Level.FINEST -> "Finest"
    else -> "Something else"
}

// levelString == "Finest"

Pretty clean right? Notice how we used when as an expression to set levelString? That's just one way of doing it, we don't have to set levelString this way. You can set levelString in the when expression to, or return from when if that makes more sense for what you're doing.

But when doesn't always need a constant to evaluate, it can evaluate a number of expressions:

when {
    logLevel.intValue() > Level.WARNING.intValue() -> {
        doSomething()
        data = "Current log level is greater than warning"
    }
    Date().time < 0 -> print("Something went wrong")
    countString is Array<*> -> {someValue = "array"}
    evaluateSomething() -> print("Something evaluatated to true")
    else -> {print("Something else")}
}

Pretty nice right? I know it's a little messy, but hopefully you get the point I'm trying to make. You can evaluate arbitrary expressions together in a simplified manner to get to your desired outcome.

But wait… there's more!

You can simplify ranges with expressions, or look for specific values:

fun doSomething(num: Int) {
    when (num) {
        1, 2 -> print("Number is 1 or 2")
        in 3..20 -> print("Number is in the range of 3 to 20")
        !in 21..23 -> print("Number is not in range of 21 to 23")
        else -> print("Else...") // in 21..23
    }
}

Just keep in mind, there is no fall through like a switch case here. when stops on the first expression to be evaluated.

Now lets get loopy

for loops are relatively the same, but just like the rest of control flow in Kotlin can be more expressive. I'm not going to explain it too much because I'm really just throwing in a bunch of expressions you've already seen now into a series of for, but the following do exactly what you expect:

val strArray = arrayListOf("str0")

// Adds str+i to strArray
for(i in 1..20) {
    // Note use of $i to concat i with "str"!
    strArray.add("str$i")
}

// Prints each string
for(i in strArray) {
    println(i)
}

// Prints strings in the arraylist from index 1 to 5
for(i in 1..5) {
    println(strArray[i])
}

val strMap = mapOf(Pair(1, "val1"), Pair(2, "val2"))
for((key, value) in strMap) {
    println("key: $key, value: $value")
}

Pretty clear, but powerful at the same time right? I can't help but think how much time I would have saved myself if all the languages I've had to work in had for constructs like this.

It's not the end, but we've come some distance from the start

We knocked out a big chunk of what control flow looks like in Kotlin here, but we're not done yet. I debated on whether or not to go into how Kotlin implements jumps in this post. I gave in and started writing, but I came to realize its probably a little too much for this post.

For now, know that jumps are a concept in Kotlin. You'll probably see them used lightly in some projects. They're powerful in some cases, but they can also make your code harder to read and follow. I'll likely turn this into my next post. For now, here's the Kotlin documentation for ‘Returns and Jumps’.

comments powered by Disqus