Kotlin M12 is out!

Posted on by

We are happy to present Kotlin M12, bringing some rather important changes and new features:

New syntax for annotations and enums

More convenient semantics of function types

Better smart casts

kapt for Java Annotation Processing support

for Java Annotation Processing support Multiple IDE features

and more…



Language

Many of the changes introduced to the language and core libraries are deprecations. Use “Code Cleanup…” action to fix all warnings in your project automatically.

Annotations: New Syntax

As we mentioned before, we decided to reserve square brackets for some more productive future uses, and make annotation syntax more familiar to Java users. So, since M12, we write @Foo(args) instead of [Foo(args)] . More details can be found here (even more — in the spec document).

Note that @ is not required in most cases. Normally we write annotations without any escaping:

data class Foo // {{EJS0}} is an annotation

The old syntax based on [...] is deprecated, so the compiler will issue warnings on your code. To fix these warnings, press Alt+Enter and run a quick fix (individual or for the whole project). The aforementioned “Code Cleanup…” action also works for the whole project.

Label Syntax Changed

Since M12 @name is an annotation, but it had a meaning before, i.e. it was a label. We had to find some other syntax for labels, and now they are declared with @ at the end:

loop@ for (i in 1..100) { for (j in 1..100) { if (...) break@loop } }

So, loop@ declares a label, and break@loop uses it.

Class Literals in Annotations

Before M12, annotations in Kotlin were allowed to use java.lang.Class , for example:

annotation class handledBy(val handlerClass: Class<out Handler>) // Usage handledBy(javaClass<MyHandler>()) class MyEvent {...}

Now, using Java-specific classes is deprecated in Kotlin annotations, and we need to use Kotlin’s own model: kotlin.reflect.KClass instead of java.lang.Class and Foo::class instead of javaClass<Foo>() :

annotation class handledBy(val handlerClass: KClass<out Handler>) // Usage handledBy(MyHandler::class) class MyEvent {...}

Note that Kotlin sees Java annotations as if they referred to KClass instead of java.lang.Class :

// Java @interface JavaAnnotation { Class<?> value(); }

// Kotlin fun introspect(jann: JavaAnnotation) { val theClass = jann.value // the type of this expression is KClass<*> ... }

Now, when we need to turn a KClass into a java.lang.Class , we can call .java on it, e.g. Foo::class.java or jann.value.java .

Annotated Primary Constructors

We decided to make primary constructor syntax more regular, and now the full form of the primary constructor includes the constructor keyword:

class PrivateConstructor private constructor (val x: Int) { ... }

The full form is only needed when we want to annotate a primary constructor or add a modifier. In most cases, the old familiar syntax still works:

class MyClass(val x: Int) { ... }

Traits Are Now Interfaces

As our traits are rather limited anyways, and Java’s interfaces are pretty much the same thing, we have deprecated the trait keyword, so please use interface instead.

As usual, quick fixes and “Cleanup Code…” will help you along.

Enum Classes: New Syntax

The new syntax for enums is very close to what Java has. Enum entries should now be separated with commas:

enum class Foo { A, B, C }

Now, when you declare a member of an enum class, it must go after all entries, and there must be a semicolon after the last entry:

enum class FooWithMember { FOO, BAR, BAZ; fun doIt() { ... } }

Lastly, when enum has a constructor, you can call it by simply passing the arguments next to the name of the entry:

enum class Color(val rgb: Int) { RED(0xFF0000), GREEN(0x00FF00), BLUE(0x0000FF) // no members => semicolon is not needed here }

The old syntax is deprecated.

Function Types Reformed

We unified function types and extension function types, so that now they can often be used interchangeably. For example, we can pass String::length where a function '(String) -> Int' is expected.

// map() expects {{EJS1}} // argument has type {{EJS2}} strings.map(String::length)

More details in this post.

If you used Kotlin’s function classes (e.g. kotlin.Function1 ) in your Java code, you will need to make adjustments to it, because from now on these classes reside in the kotlin.jvm.functions package. You can migrate all your Java code by running “Cleanup Code…” with the “Usage of deprecated function classes in Java” inspection.

Smart Casts Made Even Smarter

A long awaited feature: Kotlin can now smart-cast local var ‘s:

var foo = bar() if (foo != null) { foo.baz() // no error here }

Of course, the smart cast only works when the compiler knows that no modification could possibly have happened since the relevant check was made. Note that loops often times distort this picture (due to some technical reasons we can not use a fully-fledged data flow analysis for smart casts), so when a var is mutated in a loop, smart casts may not work.

Usages of public and protected immutable val ‘s in the same module can also be smart-cast now:

class C(public val d: D?) fun foo(c: C) { if (c.d != null) { c.d.foo() // c.d has been smart-cast here } }

Inlining and Non-Local Returns Supported for Function Expressions

Function expressions introduced in M11 are now supported in inline calls:

fun test(list: List<Foo?>) { val mapped = list.map(fun (item) = item?.toString() ?: return@test) // non-local return ... }

Deprecations and Dropped Features

M12 removes some previously deprecated features:

class object is dropped in favor of companion object ;

is dropped in favor of ; init is now required in front of anonymous initializer blocks.

Some more features were deprecated:

Use quick-fixes and “Cleanup Code…” to migrate your programs.

Java Interop

jvmOverloads

Kotlin has default arguments that dramatically reduce the needs in overloads, but Java clients can not benefit from this feature directly.

In M12 we have added an annotation jvmOverloads that tells the compiler to generate N+1 overloads for a Kotlin function that has N default parameters.

For example,

jvmOverloads fun f(a: String, b: Int = 0, c: String = "abc") { ... }

will generate

// Java void f(String a, int b, String c) void f(String a, int b) void f(String a)

Source Maps for Better Debugging (JSR-45)

We can now step through bodies of inlined function, thanks to source mapping tables emitted by the compiler into generated class files.

Some technical notes: every class file has line numbers assigned to instructions. For inlined function bodies we assign line numbers beyond the actual end of file (e.g. if the file has 50 lines, inlined code has line numbers starting with 51), and these “virtual” numbers are mapped to actual sources of the inlined code that possibly reside in other files. The standard JVM debugger understands the source mappings and can step through the appropriate files and lines. The only caveat is that exception stack traces may sometimes contain line numbers beyond the end of file. We are looking for a solution to this problem.

Java Annotations: Argument Ordering

In Java, annotations are interfaces and their parameters are methods of those interfaces. Thus, the ordering of the parameters is insignificant, and the call site can not rely on it. This is why Java requires that all arguments but one (named value ) are passed as named.

While annotations declared in Kotlin have proper constructors that admit positioned parameters and even varargs, we can not rely on Java annotations in this respect, so from now on the following applies to Java annotations:

only the parameter named value can be passed without a name,

can be passed without a name, only the parameter named value can be a vararg (and automatically becomes one, if it is an array in Java),

can be a (and automatically becomes one, if it is an array in Java), all other parameters can not be passed as positional.

JavaScript

JavaScript back-end is catching up with the JVM one. M12 adds support for

Inlining works between modules

Reified parameters

Function expressions

Secondary constructors

Tools

kapt: Annotation Processing (JSR-269)

As mentioned earlier in this post, M12 adds initial support for Annotation Processing, so that frameworks like Dagger 2 work with Kotlin now. The main limitation of the current implementation is that Kotlin code can not refer to any declarations generated by the annotation processors (so, for Dagger you need to write at least one small class in Java).

We are going to fix this limitation in the future by generating stubs for classes emitted by the Kotlin compiler. Details in the aforementioned post.

Gradle: JUnit Support for Android

Kotlin Gradle plugin for Android now supports JUnit tests. All we need to do is follow the standard procedure for Java, but now we can write our tests in Kotlin.

Quasar Support

Some of the recent changes in Kotlin enabled a great addition to our ecosystem: now Quasar provides fibers (lightweight threads), Go-like channels, Erlang-like actors, and other asynchronous tools for Kotlin! See the announcement here.

Standard APIs Changed

M12 adds new functionality to the standard library:

new utilities in kotlin.io package

package new text utilities

regular expressions API unified across JVM and JS

new collection utilities

MIN_VALUE and MAX_VALUE available for all numeric types for both JVM and JS

As we are working on the Kotlin standard library, some things get changed and/or deprecated. Use quick fixes and the “Cleanup Code…” action to migrate your code. (Please make sure that you have attached the sources of the standard library to your project.)

The full list of changes is available here.

IntelliJ IDEA Plugin

Kotlin IDE now supports the standard Introduce Parameter refactoring that turns an expression selected inside a function into a parameter:

Additionally, Introduce Lambda Parameter is available to extract a piece of code as a function value:

Rename has an option to rename related declarations (variables, subclasses etc) as well:

As we are adding a lot of deprecations lately, the IDE now supports ReplaceWith quick-fix: there’s an (optional) extra parameter to the deprecated annotation, where we can specify an expression to replace a deprecated call:

There is an intention action to add ReplaceWith to user’s deprecated declarations.

Some more changes:

New Debugger Features Evaluate expression for local functions Field Watch Points (only for properties with backing field)

Change Package Intention

Highlighting exit points of functions

Gutter Marks for recursive calls

Unused receiver parameter inspection

Code style settings for imports (e.g. we can now always import specified packages with ‘*’)

Java2Kotlin Converter now offers to update usages in other files

Typing ‘!’ when completion list is open inserts negated call (e.g. !foo.isEmpty() )

) Intention actions to change visibility modifiers

Intention actions have much better scope of availability now

Quick-fix to add parameters from the base class when the superclass’ constructor has parameters

Things That Haven’t Made It (Yet)

We are still working on some of the changes we announced earlier, such as More Null-safety for Java. Will roll them out as soon as they are ready.

More Announcements Coming

There will be some more M12-related material published in the nearest future. Stay tuned!