Kotlin logo

Kotlin

A concise multiplatform language developed by JetBrains

Language design

Making Platform Interop even smoother

Being 100% interoperable with the JVM, and subsequently with JavaScript, has always been among Kotlin’s top priorities. With the amount of existing code, and a rich JVM ecosystem, having the ability of interoperating and leveraging these is crucial.

With the upcoming release of M9 we’ve improved on this, making the integration even more seamless.

Platform Types

Dealing with nulls is one of the biggest issues when it comes to Java  interoperability. Almost any Java method may, potentially, return null, so Kotlin has treated Java types as nullable, and we either need to resort to using the safe-call (?.) operator or notnull-assertion (!!) to avoid compilation errors:

val x = javaCanReturnNull() // Java call that can return null

on trying to pass the value x to the following functions:

fun nullNotAllowed(value: String) {}
fun nullAllowed(value: String?) {}

in the first case, Kotlin compiler would issue an error. This means that the call to nullNotAllowed would need to be:

val x = javaCanReturnNull()
nullNotAllowed(x!!)

As of M9 this is no longer the case. This allows for much cleaner code, and avoiding the overuse of ?. and !! operators when interacting with Java.

Much the same way, when implementing Java interfaces, methods that can have potentially null arguments, no longer require these declarations to be declared as nullable in Kotlin. For instance, given:

public interface SomeFoo {
    void foo(String input, String data);
}

when implementing this in Kotlin:

public class SomeInterestingFoo(): SomeFoo {
    override fun foo(input: String, data: String?) { // String? no longer required
    ...
    }
}

the parameter input no longer has to be of type String?. You can choose to make it either String or String? — depending on its actual meaning. In this example we chose input to be not-null, and data to be nullable.

Annotating methods with platformStatic

Kotlin has object declarations which can be viewed as singletons. And these are consumable from Java, albeit not with the nicest syntax. Given:

object Foo {
    fun bar() {
    }
}

consuming this from Kotlin, would be:

Foo.bar()

whereas from Java it would look like this:

Foo.INSTANCE$.bar()

With the next release, we’ll be able to call the method from Java the same way as from Kotlin by simply adding an annotation to the function declaration:

object Foo { 
   platformStatic bar() {
   }
}

making the code much cleaner. Same applies to class objects.

Removing roadblocks

Another benefit of platformStatic is removing some showstoppers that existed when using certain Java libraries such as JUnit. In particular, the latter requires a static method in Java when using Theories. The workaround for this was quite tedious. Fortunately, this is no longer the case. We can use the platformStatic annotation to solve this issue.

RunWith(javaClass())
public class TestDoubling {
       class object {
           platformStatic DataPoints
               public fun values(): IntArray {
                    return intArray(-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
               }
         }
 
         Theory
         public fun testDoubling(a: Int?) {
                  Assert.assertThat(doubling(a!!), `is`(a * 2))
         }
 
         public fun doubling(value: Int): Int {
                  return value * 2
         }
}

Leveraging overloaded functions with platformName

When having overloaded methods that use generics such as:

fun Iterable.average(): Double {
...
}

fun Iterable.average(): Double {
...
}

While calling these from Kotlin is possible, trying to invoke these from Java is problematic due to type erasure. Similar to platformStatic, we’ve introduced the platformName annotation that allows to rename the function so that when invoked from Java, the new given name is used:

fun Iterable.average(): Long {
...
}

platformName("averageOfInt") fun Iterable.average(): Int {
...
}

This can now be called from Java as follows:

averageOfInt(numbers)

Note that this is not the only use case for platformName.

Private property accessors

Property accessors are no longer generated for private private properties in Kotlin, which means that conflicts with existing getXYZ methods do
not occur if unnecessarily. Take the following interface in Java:

public interface SomeFoo {
    String getBar();
}

If we are to implement this interface in Kotlin and our class has a private property named bar, in M8 and previous versions it causes a conflict, and we’d have to name the property to something different than bar. Starting with M9, this is no longer the case. As such, the code below is perfectly valid:

public class SomeInterestingFoo(private val bar: String): SomeFoo {
    override fun getBar(): String { 
          ...
    }
}

Summary

With these changes coming in M9 we’ve removed some roadblocks and more importantly aimed at making the interoperability between Java and Kotlin much cleaner. And while these improvements can make consuming existing Java code more pleasant, it also allows for an even better experience of writing new libraries and functionality in Kotlin and consuming these from Java.

As always, we’d love feedback. Let us know what you think.

Note: M9 has not been released yet, but you can find these features on the nightly builds

image description