M8 is out!

Posted on by

It’s been a really busy couple of months since the last release and we’ve been working hard on making substantial improvements, particularly in terms of speed. We have a lot of goodies for this release. Let’s get started

JVM

Reflection for properties

As a first peek into the future reflective capabilities of Kotlin, you can now access properties as first-class objects in Kotlin:

var x = 1 fun main(args: Array ) { println(::x.get()) // prints "1" ::x.set(2) println(x) // prints "2" }

The “::propertyName” syntax gives a property object so you can get or set its value. You can also access the name of the property (will be useful for frameworks of all sorts). We will add more capabilities in the future.

To access a property that is a member of a class, you can say

class A(val p: Int) fun main(args: Array ) { val prop = A::p println(prop.get(A(1))) // prints "1" }

And, of course, it works for extensions as well:

val String.lastChar: Char get() = this[size - 1] fun main(args: Array ) { println(String::lastChar.get("abc")) // prints "c" }

Another side of Kotlin’s reflection is its interoperability with Java’s reflection. For example, to find a backing field or a Java method that serves as a getter for a Kotlin property, you can say something like this:

import kotlin.reflect.jvm.* class A(val p: Int) fun main(args: Array ) { println(A::p.javaGetter) // prints "public final int A.getP()" println(A::p.javaField) // prints "private final int A.p" }

We will develop reflection capabilities further in the next few months. The ultimate goal is to provide framework writers with really powerful tools for their needs. Things on the agenda include proper Kotlin class introspection, making types available through reflection, bringing reflection capabilities to callable references (::functionName) and more. Stay tuned.

Inline Function Improvements

Some enhances for inlining functions including:

Support for functions with default parameters.

Inlines into objects are supported.

To demonstrate these two features together, let’s look at the following example. Say, we have an Value interface:

trait Value { fun get() : V }

Now, let’s say we want to create values whose computations are guarded by a lock:

inline fun guardedValue( lock: Lock = ReentrantLock(), compute: () -> T ) : Value { return object : Value { override fun get(): T { return lock.withLock { compute() } } } }

Our guardedValue() is an inline function. Now, the lock parameter has a default value (a new ReentrantLock is created for every value). Then, the object expression inside this function “captures” the compute parameter, and inlines it directly inside the anonymous class created. This results in only one class and one object emitted (and stored) per value, instead of two classes and two objects for the non-inline case.

Reporting platform signature clashes

The compiler now emits errors when some JVM signatures it generates are going to clash in the byte code (previously this could lead to ClassFormatError’s or to accidentally overriding a superclass’ method without knowing it).

For example, the following Kotlin code looks harmless, but breaks on the JVM:

val x = 1 fun getX() = 1

Now, Kotlin complains about this code:

Error:(1, 1) Kotlin: Platform declaration clash: The following declarations have the same JVM signature (getX()I): fun (): kotlin.Int fun getX(): kotlin.Int

The reason is that the getter for x has the same name (getX) and signature (takes no parameters, returns Int) as the function declared next to it and JVM does not allow such things in class files.

Another case is an accidental override:

open class Base { fun getX() = 1 } class Derived(val x: Int) : Base()

Here, the getter for x again has the same signature as getX(), but they are now in different classes, so the getter would silently override the function, so that Derived(2).getX() would return 2 instead of 1.

Now the compiler catches this and the error is

Error:(1, 15) Kotlin: Accidental override: The following declarations have the same JVM signature (getX()I): fun (): kotlin.Int fun getX(): kotlin.Int

One may wonder why wouldn’t we, say, rename one of the functions automatically instead of emitting an error? The answers lie in the realm of Java interoperability (nobody likes names invented by the compiler) and binary compatibility (names of things must never change silently, or all dependent code will unexpectedly break). On the other hand, you can specify desired JVM names manually with the platformName annotation:

import kotlin.platform.platformName val x = 1 [platformName("doGetX")] fun getX() = 1

Note that nothing changes when for the Kotlin clients of this code, but Java clients must use the platform name (doGetX) instead of Kotlin name (getX).

WARNING: This is a breaking change, some of your existing code may need to be fixed.

Support for transient, synchronized and strictfp

In addition to the long-available volatile. We now support transient, synchronized and strictfp as annotations. The semantics are exactly like in Java:

class FlagDemo { volatile var s = "" transient var list = listOf(1, 2, 3) strictfp fun math() { /* strict floating-point available here */ } synchronized fun sync() { /* synchronized on this */ } }

Generated code speedup

Generated code for delegated properties works much faster now, and make a lot fewer memory allocations.

In addition when statements with constants are faster because of dedicated bytecode instructions now being used.

JavaScript Support

You now have Data Classes support in JavaScript. This means that you can now get toString, equals and hashCode generated automatically when annotating classes with the data annotation in JavaScript too, as well as in Java.

In addition, LinkedHashSet and LinkedHashMap are also supported in JavaScript.

Language Changes

A few language changes, all of which are breaking changes, so important to take note:

private in a package now means private to the current package inside the module . Packages with the same name in other modules won’t see these definitions. Note that packages are nested, so privates are also visible to subpackages in the same module.

in a package now means private to the current package . Packages with the same name in other modules won’t see these definitions. Note that packages are nested, so privates are also visible to subpackages in the same module. Extension Properties cannot have backing fields. This was never recommended anyway.

If you ever discovered that Kotlin allowed “@” and “@@” as label names, we are sorry to tell you that this is no longer so.

Additions to Standard Library

The standard library gets some new functionality also:

slice() function added that takes an iterable of integers and returns a list containing the elements in said positions.

join() works on iterables (array, lists, etc.) of strings and combines them into one string

joinToString() works on iterables or arrays of any type and is equivalent to .map { it.toString() }.join()

Maps now have overloaded functions contains()., thus enabling things like “key in map” by convention

Strings now can take advantage of collection like functions including substringBefore, substringBeforeLast and substringAfter, substringAfterLast allowing to find string before and after a certain character or string. replaceBefore, replaceBeforeLast and replaceAfter, replaceAfterLast allowing to replace strings before and after a certain character or string. appendln added to StringBuilder A new StringBuilder function that takes a String builder extensions, allowing for code such as: val information = StringBuilder { append("A first entry") appendln() appendln("Some other line") }



Iterables now have merge function which returns a new list containing elements merged from two collections on which a transformation has been applied public inline fun Iterable .merge(: Iterable , transform: (T, R) -> V): List This is different to zip in that merge would be: collection.merge(anotherCollection) { (v1, v2) -> v } and zip would be: val zip = merge { v1, v2 -> v1 to v2 }

Support for set operations such as union, intersect and substract.

See API docs for more details.

IntelliJ IDEA 14 EAP support and more

In addition to supporting the recently opened IntelliJ IDEA 14 EAP, we have a lot of new IDE features:

Debugger Improvements

Ability to do Smart Step Into

Evaluate Expression is now supported

New Refactorings

Move Top Level Declarations to a new file

Top Level Declarations to a new file Extract Function . Still needs some improvements, but works in many cases.

. Still needs some improvements, but works in many cases. Extract Local Function.

New Intentions

We love intentions. We believe that they not only quickly help you solve problems, but also teach you and help you discover language and framework constructs. With this release we’ve added 14 new intentions, including:

DeMorgan’s Law



Flip binary expression

Split if conditionals

Replace with operator sign

Replace with traditional assignment

Replace with infix call



Simplify boolean expressions

Add/Remove explicit type arguments

Convert assert to if with throw and viceversa

Convert if to ?: and viceversa

Convert !! to if and viceversa

Make types explicit/implicit in lambdas

Convert forEach { } to a for loop and viceversa

Convert String concatenation to interpolation and viceversa



Smart Completion Improvements

More improvements when it comes to Smart Completion with support for:

Smarter completion on overloaded methods.

Callable references now show up in smart completion, meaning you can get completion for ::functionNames.

Completion in Lambdas.

!! and ?: for nullable members

Completion in Anonymous classes (object expressions)

Completion for constants in when() statements

Other IDE Improvements

In addition to the above, we’ve also:

Improved Formatter

Structure View now shows members of supertypes

now shows members of supertypes Speedups in highlighting and completion

Java to Kotlin Converter improved

Finally, we now also support the latest versions of Gradle and Android Studio (0.10, 0.11 and 0.12).

Download the latest release and as always, feedback welcome! IntelliJ EAP 14 already includes the latest plugin, and as always you can find the plugin on our plugin repository.