10 Peculiarities in Kotlin

10 Peculiarities in Kotlin

Avatar von Christopher Stock

Kotlin is among the most popular programming languages and is presumed to be the alternative solution to Java. Find a quick introduction to Kotlin here and gain insights into the ten most peculiarities and syntactical improvements that advantage Kotlin over Java.


What is Kotlin?

Kotlin is a modern, typed, and well-readable programming language and a drop-in replacement for Java. Source code written in Kotlin is translated into JVM-Bytecode that can be invoked from existing Java source code. This facilitates a seamless introduction of Kotlin into existing Java projects.

Kotlin offers numerous syntax improvements that eliminate the redundant formulation of established Java code constructs. Simultaneously, the language offers new pragmatic code features that, for instance, avoid typical errors like NullPointer-exceptions or unwanted derivation.

1. No need for Semicolons

Kotlin does not require a trailing semicolon at the end of the line. A semicolon is only required to separate multiple statements inside the same line, which is strongly discouraged by most coding styles.

    // logs one line of output to the debug console
    println( "Welcome to Kotlin" )

    // logs three lines of output to the debug console
    println( "Kotlin" ); println( "definitely" ); println( "rocks!" )

2. Type Inference

Kotlin scrapes redundant definitions of type declarations. As long as the compiler is able to infer a variable’s required data type from the assigned value, the type is assigned implicitly.

    // type 'String' is inferred
    val message = "Kotlin rules!"

Adding an explicit data type to a variable is optional on declaring it. The following code is therefore also valid.

    // type 'String' is explicitly declared
    val message: String = "Kotlin rules!"

3. Default Values for Function Parameters

Kotlin allows the definition of default values in a function’s parameter list. That makes Java’s common used procedure of supercharging methods in order to facilitate default parameter values obsolete.

    fun createRect( x: Double, y: Double, width: Double = 1.0, height: Double = 1.0 ) :java.awt.Rectangle
    {
        val rect = java.awt.Rectangle()
        rect.setFrame( x, y, width, height )

        return rect
    }

    fun main()
    {
        // parameters 'width' and 'height' are set implicitly
        val playerRect = createRect( 17.0, 20.0 )

        // logs '1.0' to the debug console
        print( playerRect.width )
    }

In Java code, you need to supercharge the function createRect and define it multiple times. Though every method signature must be unique, the first two definitions would clash. The same example in Java would not only turn out extensively but won’t compile at all due to duplicate method signatures.

    private static java.awt.Rectangle createRect( double x, double y, double width )
    {
        return createRect( x, y, width, 1.0 );
    }

    // won't compile: ambigious method signature!

    private static java.awt.Rectangle createRect( double x, double y, double height )
    {
        return createRect( x, y, 1.0, height );
    }

    private static java.awt.Rectangle createRect( double x, double y, double width, double height )
    {
        java.awt.Rectangle rect = new java.awt.Rectangle();
        rect.setFrame( x, y, width, height );

        return rect;
    }

4. Named Arguments

Kotlin provides the optional indication of parameter names in function calls. This indeed requires the definition of more code but still positively impacts the code’s readability.

    fun interpolate( start: Double, end: Double, ratio: Double ) :Double
    {
        return ( start + ( ( end - start ) * ratio ) )
    }

    fun main()
    {
        // parameter names may be specified
        val result :Double = interpolate( start = 10.0, end = 20.0, ratio = 0.75 )

        // logs '17.5' to the debug console
        print( result )
    }

Furthermore, the explicit designation of the parameter name can be used in order to assign the desired parameter. This is important when more than one parameter has a default value.

    fun createRect(
        x:      Double = 1.0,
        y:      Double = 1.0,
        width:  Double = 1.0,
        height: Double = 1.0
    )
    :java.awt.Rectangle
    {
        val rect = java.awt.Rectangle()
        rect.setFrame( x, y, width, height )

        return rect
    }

    fun main()
    {
        // parameter 'y' is set implicitly
        val playerRect = createRect( y = 2.0 )
    }

5. Flexible „when“-construct replacing „switch“

Kotlin’s when-Statement is similar to the switch-statement in Java, but it allows a wider range of usages. Kotlin allows a flexible examination with many different expressions. The syntax of the corresponding case labels is much more purified than in Java.

    when ( age )
    {
        0         -> println( "is 0"                    )
        12, 16    -> println( "value of 12 or 16"       )
        in 3..10  -> println( "in range 3 to 10"        )
        in ages   -> println( "contained in collection" )
        is Number -> println( "type is Number"          )
        else      -> println( "default case"            )
    }

The when-construct can also be used to assign an expression to a variable or constant directly. That helps you to avoid redundant code via temporary variables.

    val fskColor = when ( age )
    {
        in 0..6   -> WHITE
        in 6..11  -> YELLOW
        in 12..15 -> GREEN
        in 16..17 -> BLUE
        else      -> RED
    }

The when-Statement is also preferred over chains of if-else-constructs and will improve its readability.

    when
    {
        age < 18  -> println( "Age is below 18" )
        age % 2 == 0 -> println( "Age is even"     )
        else         -> println( "Age is odd"      )
    }

Kotlin’s when statement does not implement a fall-through behaviour like in Java’s switch statement. Therefore, Kotlin does not implement the error-prone break-statement here.

6. Slight Class Constructs

Classes may be specified in Kotlin, supplying a primary constructor. Non-static class member fields can directly be declared inside this constructor. All fields may be declared mutable or constant using the keywords var and val. The visibility for all members defaults to public in Kotlin, not to package-private as in Java.

    class Guest
    (
        val firstName: String,
        val lastName:  String,
        val age:       Int
    )

Instances of this class can be created in the same way as in Java, though Kotlin omits the new statement. Though all fields have been declared read-only in class Guest, none of them can be assigned with a new value.

    val guest = Guest( "Christopher", "Stock", 39 )
    println( "Age of the guest is [" + guest.age + "]" )

    // won't compile! age is a read-only property
    guest.age = 18

In order to achieve the same functionality in Java, the access to all fields must be encapsulated by turning all fields private and supplying getters but no setters. The equivalent class would look as follows in Java:

    public class Guest
    {
        private String firstName;
        private String lastName;
        private int    age;

        public Guest( String firstName, String lastName, int age )
        {
            this.firstName = firstName;
            this.lastName  = lastName;
            this.age       = age;
        }

        public String getFirstName()
        {
            return this.firstName;
        }

        public String getLastName()
        {
            return this.lastName;
        }

        public int getAge()
        {
            return this.age;
        }
    }

7. String Templates

Handling strings has been simplified in Kotlin, and the readability of string operations has been improved. Kotlin enables strings with enclosed variables in the following way:

    println( "My name is $firstName $lastName and I'm $age years old." )

You may also use functions or any other expression inside string literals.

 
    println( "My name is ${ firstName.toUpperCase() }." )
 

8. Nullables

Tony Hoare, the inventor of the null construct, once denominated his invention as „The Billion Dollar Mistake.“ The employment of null is extremely fault-prone and has caused horrendous costs over the last decades.

The notorious Java NullPointerException widely belongs to the past among Kotlin. Kotlin implements null as a part of its type system, so every variable must explicitly specify whether it may be assigned with null. Types that permit this behavior are named Nullables and are denoted with a trailing question mark according to the data type.

    var firstName: String = "Christopher" // firstName is non-nullable
    firstName = null                      // won't compile!

    var lastName: String? = "Stock"       // lastName is nullable
    lastName = null                       // compiles smooth

The Safe-Call-operator ? invokes a non-static method as long as the object is not null. If the object is null, the method will not be performed, and in addition, null is returned. None of the two cases throws a NullPointerException.

    val firstName: String? = "Christopher"
    val lastName:  String? = null

    println( firstName?.toUpperCase() )    // prints "CHRISTOPHER"
    println( lastName?.toUpperCase()  )    // prints "null"

Using the Elvis-operators ?: allows you to assign an alternative value, as long as the variable is null and must be converted into a non-null data type.

    fun displayName( name: String? )
    {
        val nameToDisplay: String = ( name ?: "default name" )

        println( nameToDisplay  )
    }

Even though these instructions may look quite a bit hard to get used to, their advantage is plain to see: Already, the compiler detects and checks the potential presence and assignment of nullvalues, so the developer doesn’t need to implement checks for null where they are unessential or add try-catch-constructs in order to catch NullPointerExceptions.

9. No unwanted Derivation

All classes are implicitly final in Kotlin. Extending one class must explicitly be permitted by preceding the open modifier. Furthermore, Kotlin replaced the Java keywords extends and implements with a colon.

    class Fruit

    class Apple : Fruit() // won't compile!

This strict specification helps you to avoid errors from unwanted derivation and works oppositely as Java’s final modifier.

    open class Vegetable

    class Carrot : Vegetable() // compiles smooth

10. Open for Extension

All functions and types can be extended in Kotlin without deriving a separate compilation unit. Each existing element can be enriched with additional functionality, no matter if this is about an own or an existing element. Please find a simple example of expanding the native class String.

    fun String.duplicate() : String
    {
        return ( this + this )
    }

    fun main()
    {
        // outputs "HiHi"
        println( "Hi".duplicate() )
    }

So Kotlin!

These were some of Kotlin’s features and syntactical improvements compared to Java. In my opinion, Kotlin is a very well-designed and sophisticated programming language that extends all of Java’s potential and additionally offers many clever code constructs composed in a very legible and slight syntax.

Though Kotlin brings all named functionality to the JVM in a simple and accessible manner, it represents a great alternative to Java.

I’d be thrilled if I could give you a quick introduction to this exciting programming language. Maybe I was able to inspire you to try Kotlin in practice. Especially Java and Android developers may greatly benefit from Kotlin.

Be welcome to send me feedback or inquiries concerning Kotlin or this article via christopher.stock@mayflower.de.

Avatar von Christopher Stock

Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert


Für das Handling unseres Newsletters nutzen wir den Dienst HubSpot. Mehr Informationen, insbesondere auch zu Deinem Widerrufsrecht, kannst Du jederzeit unserer Datenschutzerklärung entnehmen.