Kotlin logo

Kotlin

A concise multiplatform language developed by JetBrains

Releases

M9 is here!

M9 has arrived and it’s bringing many new features and important changes. We’ve already highlighted these and covered others in detail. Let’s dig deeper into some of the other improvements.

Language Changes

NOTE: Some of the changes below are breaking changes, meaning that some code that was compilable with earlier versions will not compile any more, so you’ll need to correct it.

Platform Types

As we mentioned before, platform interoperability (i.e. Java and JavaScript interoperability) is among our top priorities, because this is so for our users. Nullability issues when interoperating with Java were one of the biggest complaints we were getting. In a nutshell, the problem is that any reference coming from Java may be null, and Kotlin, being null-safe by design, forced the user to null-check every Java value, or use safe calls (?.) or not-null assertions (!!). Those being very handy features in the pure Kotlin world, tend to turn into a disaster when you have to use them too often in the Kotlin/Java setting. We relied on external annotations and KAnnotator to mitigate this problem by enhancing Java with extra type information. This approach proves to be too cumbersome and doesn’t work for some cases.

This is why we took a radical approach and made Kotlin’s type system more relaxed when is comes to Java interop: now references coming from Java have specially marked types (we call them “platform types”, because they come from the underlying platform), which are treated specially:

  • Kotlin does not enforce null-safety for platfrom types. I.e. for Java values you get Java’s semantics: NPE is now possible for values coming from Java
    val s = javaMethod() // s has a platform type
    s.foo() // this line may throw NPE, like in Java
    
  • Platform types can not be mentioned explicitly in Kotlin code, but when the IDE/Compiler shows you type information, they are distinguished by exclamation marks at the end. Examples: String!, ArrayList<Int!>!, (Mutable)Collection<Foo!>!.
  • To store platform values, you can either rely on the type inference, or pick Kotlin types (either nullable or not, as you please):
    val s = javaMethod() // platform type inferred
    val s1: String = javaMethod() // not-null type chosen by the programmer
    val s2: String? = javaMethod() // nullable type chosen by the programmer
    

    When you assign a platform value to a non-null type, Kotlin emits assertions in the byte code so that to make sure that a non-null variable doesn’t hold a null, this keeps most nulls from propagating through the code. So, there may be an assertion error on line 2 in this example, instead of an NPE some time later.

  • When you override Java methods, again, you can not mention platform types, so you have to pick some Kotlin types instead:
    class KotlinClass : JavaClass() {
        // declared in Java as 'List javaMethod(String s)'
        override fun javaMethod(p1: String): List { ... }
    
        // or it could be
        override fun javaMethod(p1: String?): MutableList?
    
        // or any other combination of nullabilities and collection mutability
    }
    

To summarize, platform types relax Kotlin’s type system for the case of things coming from Java. When you are using Java libraries, Kotlin is as safe as Java. When there’s no Java involved, Kotlin is even safer: it guarantees absence of NullPointerExceptions. In the future, we are planning to enhance diagnostics on platform types so that Kotlin will issue warnings on misuse of nulls in the Java interoperability cases.

More on Platform Interop

We already covered in detail the changes introduced for better platform interoperability, but just to recap, we have introduced the platformStatic annotation to define methods as static when being called from Java, as well as a series of improvements for better dealing with getters, setters and other interop aspects.

No more local object declarations

As of M9, named objects can no longer be local. To use an object inside a function we need to use object expressions, i.e. assign an unnamed object to a variable:

fun usingObjects() {
  val configuration = object  { 
      val maxRows = 10
  }
}

The reason for this is that named objects in Kotlin are meant for representing singletons. Having these inside functions, which can be called several times makes little sense. Named objects can still exist if they are global or nested in non-inner classes.

Non-local returns

When working with higher-order inline functions we can now do non-local returns:

inline fun using(closeable : Closeable, body : ()->Unit) {
    try {
        body()
    } finally {
        closeable.close()
    }
}

fun processStream(stream : InputStream) {
    using(stream) {
        if (stream.read() == 42)
            return // exits processStream
    }
}

Calling return from inside the function being passed to using, will exit the outer function processStream. In the case where the return type of processStream is different to Unit, the return must also provide a value.

Type is no longer a keyword

We can now use the word type as an identifier as it’s no longer a keyword.

Unit is now a named object

What used to be called Unit.VALUE is now called simple Unit.

Foo.instance$ becomes Foo.INSTANCE$

In the Java code we used to access Kotlin’s singletons (named objects) as Foo.instance$, now it’s Foo.INSTANCE$.
Similarly for class objects: was C.object$, now C.OBJECT$.

Incremental Builds

Kotlin’s goal has always been to be as fast as Java, and while we’re not there yet, we have taken one major step, and that is to provide incremental builds. In IntelliJ IDEA, we can toggle this option via a checkbox under Kotlin Compiler options:

Incremental

NOTE that this feature is experimental. If something doesn’t work correctly, run Rebuild Project. Bug reports are appreciated.

You can also enable making projects automatically under Compiler options:

make-options

JavaScript support

We’ve been spending a lot of effort on the JavaScript backend, sorting out language issues and bringing the standard library up to part with the Java equivalent (to the extent possible). As such, code like the following now works when targeting JavaScript:

 val cars = listOf("Ford", "Audi", "Skoda", "Toyota", "Jeep")
 cars.filter { it.contains("o") }
 cars.forEach {
     println(it)
}

In addition to improving the standard library, we’ve also improved language support, now allowing for

  • Delegation (class Foo : Bar by baz)
  • Callable references for functions and properties (::foo)
  • Support for Long and improved support for Char types
  • Inline functions support
  • Multiple catch-blocks

There have also been some breaking changes, namely:

  • Native traits exist only at compile time
  • Some changes in function name mangling
  • Moving and renaming some packages
    • js.* is now kotlin.js.*
    • js.query.* is now jquery.*
    • js.debug.* is now kotlin.js.*
  • Changes in println() behavior:
    • on node.js – write to stdout
    • otherwise (in the browser) – buffered write to console.log, flush buffer to console on “\n”

We’ll cover some more details about JavaScript separately.

IntelliJ IDEA improvements

Kotlin plugin for IntelliJ IDEA also has some new features and improvements.

Extract Function Refactoring and Code Duplication

Extract function now offers code duplication detection

create-entry

prompting us to replace similar code occurrences with the the function we’ve just extracted.

duplicate-find

Create From Usage

Long-time awaited, especially for those of us that do TDD once in a while, is the Create From Usage intention. We can now create functions based on usage, and even extension functions

create-usage

Modules

Up to recently, Kotlin would treat any module’s symbols or any library referenced from a project anywhere, visible to the entire project. For instance, if we have three modules:

  • Ordering
  • Invoicing
  • Shipping

Invoicing and Shipping depend on Ordering, but do not on each other, up to now, in the IDE, completion and autoimport would still show symbols from all modules in all modules.
As of M9, it will only show symbols from those modules that are dependant.

Find and Rename by Convention

Kotlin allows for operator overloading. For instance, we can overload the + by creating a function named plus. Now, when renaming these, IntelliJ IDEA will automatically rename all corresponding usages.

operator-rename

Much the same thing will happen when moving from it to explicit arguments in lambda expressions.

it-renaming

Other IDE improvements

In addition, there are some other improvements. In particular

  • Debugger. Now supports delegated properties.
  • Completion. Overall improvements in completion in addition to completion for as when expected types are known at the point of invocation
  • Expand selection. Offering pretty much all the same functionality we currently have for Java
  • Java to Kotlin converter. Allowing for more conversion scenarios.

as well as numerous bug fixes.

Command line interface changes

Some changes to the command line compiler also to make it simpler. We no longer have to indicate parameters -src/-sourceFiles but merely pass in options first and then source files:

kotlinc-jvm  

A few options have changed, such as -output and -jar and a few others. Running

kotlinc-jvm --help

will provide all options. In addition, some advanced settings have been hidden under the -X parameter. To obtain we simply execute:

kotlinc-jvm -X

Lastly we also prettified some of the output to indicate relative paths versus absolute.

JVM Code Generation and Performance Improvements

As mentioned previously, we’ve also been working on improving code generation for JVM, reducing byte code size and increasing performance of generated code.

API Documentation

In addition to Kotlin itself, we’ve also worked on moving the older API reference documentation from the old format to a completely new revamped format using markdown.

You can find the API reference on the new site.

Summary

M9 is an important milestone, primarily because of the platform types. For us it’s really important to get feedback to determine whether these changes are in the right direction and if they’re making interop easier. So please try it out and let us know.

To install M9, update the plugin in your IntelliJ IDEA 13.1 or 14 EAP, and as always you can find the plugin in our plugin repository.
NOTE you may need to invalidate caches of the IDE and/or rebuild your project.

Check out the release page and as always, feedback welcome!

image description