Joao Alves

Engineering Manager at IndeedFlex

Kotlin Syntax Part II — when did this switch happen?

Posted at — Jan 3, 2018

image

Hey everyone, hope you had an amazing Christmas and an awesome new year’s eve. Welcome to article number 3 of the series and the first one of 2018. In the previous article we went through some of the Kotlin Base Syntax, today is part II of the syntax mini-series, and we’ll look into a few more examples of Kotlin syntax in action.

To recap, in the previous article we talked about Classes, Functions, Variables, Nullability and about the poor semi-colon operator almost not being needed anymore.

Conditional expressions

Today we’ll start by looking into conditional expressions. If you remember the section about Functions, we saw that functions can work as expressions in Kotlin. Well, the same thing is true for conditional expressions, let’s see:

fun biggestWord(someWord: String, anotherWord: String): String {
    if (someWord.length > anotherWord.length) {
        return someWord
    } else {
        return anotherWord
    }
}

If we want a function that returns the biggest of 2 Strings, it would look something like the above. But because if conditions can be used as expressions we can make that code better:

fun biggestWordOneLine(someWord: String, anotherWord: String): String {
    return if (someWord.length > anotherWord.length) someWord else anotherWord
}

Much cleaner right? With Koltin we can just return the if expression straight away, no need for all if and else with curly braces in multiple lines.

fun biggestWordAsExpression(someWord: String, anotherWord: String) =
        if (someWord.length > anotherWord.length) someWord else anotherWord

But as we saw, functions also work as expressions so we can drop the function curly braces and the return keyword as well leaving us with the code above. Even cleaner.

When

Another big change in syntax when switching from Java to Kotlin is the end of the switch statement. This was replaced with when in Kotlin and as with the if statement it can also be used as an expression.

Similar to the switch statement, every case is evaluated sequentially until one of the conditions is satisfied, and if none is, then the else case is returned. Looking at the example below if we pass 1 into the when it will return "One" only and not "Not a String" that is actually true as well.

If we use when as a statement it works the same way as the switch statement, if we use it as an expression the result is the value of the satisfied case. Let’s see the below code example from the official Kotlin documentation:

fun describe(obj: Any): String =
    when (obj) {
        1 -> "One"
        "Hello" -> "Greeting"
        is Long -> "Long"
        !is String -> "Not a string"
        else -> "Unknown"
    }

As we can see, we’re using when directly as an expression of a function. As opposed to the switch statement that has some limitations regarding supported types, with when we can pass any type and we can even pass the type Any that corresponds to the Object type in Java.

And because the type is Any we can try and evaluate every case by simply testing the values with no casts at all. And look how easy is to do instance of in Kotlin, an is it’s all we need.

One important thing to note is that the else branch is not always mandatory. If you’re using when as a statement you don’t need to use it, and if you’re using it as an expression you only need to use it if you don’t cover all possible cases.

I would say that a good practice is to always use it anyway. The code is more readable and it’s protected for unexpected values that can come into your function.

Another good example where when is nice to use, is to replace a if-else-if chain:

var number = 234

// traditional if-else-if chain
if (number > 0) {
    print("number is bigger than 0")
} else if (number < 0) {
    print("number is lower than 0")
} else {
    print("number is 0")
}

// using when to replace the above chain
when {
    number > 0 -> print("number is bigger than 0")
    number < 0 -> print("number is lower than 0")
    else -> print("number is 0")
}

So, looking at the code above we can see a traditional if-else-if chain that we would use in Java and then below the version using when that I personally think looks much better. By passing no parameters to the when expression, every branch conditions is treated as a boolean expression, so the branch that returns true is executed.

If you try and write the if-else-if chain as above, Android Studio will actually suggest you to convert the code to use when instead.

Ranges

A cool feature that Kotlin also has are range expressions. Ranges basically represent simple closed intervals and can be used with any comparable type, but its implementation is optimised when used with integral primitives types. There are some nice operations we can do with ranges let’s see some of them:

fun simpleRange(number: Number) {
    if (number in 1..100)
        print(number)
}

fun simpleRangeReverseWrong() {
    for (number in 100..1)
        print(number) // this doesn't print anything
}

fun simpleRangeReverseCorrect() {
    for (number in 100 downTo 1)
        print(number)
}

Cool, so if we want to print a number if it’s between 1 and 100 we can use the in and .. operators. If we wanted the opposite we could use !in and it would print the number if this was not in the 1 to 100 range. These are probably the 3 main operators when working with ranges but it doesn’t cover everything.

Another simple example is if we wanted to print all numbers from 100 to 1. If we use the .. operator we don’t get any compilation error but we also don’t get anything printed as the way the .. operator works is by using a contains function within a start and endInclusive endpoints.

So if we want to print the numbers from 100 to 1 we need to use the downTo operators that allows us to run through the range in reverse order.

fun until() {
    for (i in 1 until 100) {
        println(i)
    }
}

fun steps() {
    for (i in 1..100 step 2) {
        println(i)
    }

    for (i in 100 downTo 1 step 5) {
        println(i)
    }
}

We can also print all numbers from 1 to 99 if we use the until operator. It’s similar to .. but it excludes the last number on the range leaving the 100 out.

And what if we want to print all the odd numbers from 1 to 100 or all 5 multiples from 100 to 1? Easy, we can use the step operator. It’s basically the same as what we do with the last section of a for statement in Java. If we don’t use step it’s the same as using i++ and if we use step 2 for example, it’s the same as using i+2 . As we can see above, step can be used both with the .. and the downTo operators.

Smart casts

We saw an example above while looking at the when expression, of a couple of type checks examples with the is and !is operators. As explained above, those replace the use of instance of we have in Java.

But the big thing about casting in Kotlin are smart casts. Most of the times we don’t need to explicitly use cast operators as the compiler tracks the outcome of the is checks and automatically inserts the casts automatically. Let’s see some examples:

fun simple(value: Any) {
    if (value is Int) {
        print(value == 0) // value is automatically cast to Int
    }
}

fun withReturn(value: Any) {
    if (value !is Int) return
    print(value == 0) // value is automatically cast to Int
}

fun withOr(value: Any) {
    // value is automatically cast to Int on the right-hand side of `||`
    if (value !is Int || value == 0) return
}

fun withAnd(value: Any) {
    // value is automatically cast to Int on the right-hand side of `&&`
    if (value is Int && value > 1) {
        print(value > 1) // value is automatically cast to Int
    }
}

As we can see in the first example, we’re using is to check if the value is of type Int and then we can straight away print the result of the expression value == 0 without casting value to Int, as Kotlin does it for us automatically, neat right?

Even better it works in the opposite case as well.On the second example, we just return if value is not of Int type or continue if it is. Again, because the code reached the print statement Kotlin already knows that value is an Int so it automatically casts it for us and no need to do a Java like (Int) value explicit cast.

Wait for it, it also works with || and && expressions as we can see in the bottom 2 examples. Kotlin automatically casts value on the right-hand side of the expression based on the result of the left-hand side. And in the last example, again we can see the automatic cast in action in the print function.

This is a very cool Kotlin feature that will save us a lot of boilerplate code we need in Java whenever we’re casting stuff. No more hurting our eyes with a load of () all over the place.

And that’s it for now if you enjoyed the article don’t forget 👏 and please share your ideas and comments. The code used for this article is also under the syntax package. Series Github project link below. See you at number 4 👋

Kotlin Syntax Part I — why am I excluded? ⇦ PREVIOUS

NEXT ⇨ Kotlin data classes — enough boilerplate


comments powered by Disqus