Multiplatform projects are a new experimental feature in Kotlin 1.2. All of the language and tooling features described in this document are subject to change in future Kotlin versions.
A Kotlin multiplatform project allows you to compile the same code to multiple target platforms. At this time supported target platforms are the JVM and JS, with Native to be added later.
A multiplatform project consists of three types of modules:
A common module can depend only on other common modules and libraries, including the common version of the Kotlin standard library (kotlin-stdlib-common
). Common modules contain only Kotlin code, and not code in any other languages.
A platform module can depend on any modules and libraries available on the given platform (including Java libraries in case of Kotlin/JVM and JS libraries for Kotlin/JS). Platform modules targeting Kotlin/JVM can also contain code in Java and other JVM languages.
Compiling a common module produces a special metadata file containing all the declarations in the module. Compiling a platform module produces target-specific code (JVM bytecode or JS source code) for the code in the platform module as well as the common module that it implements.
Therefore, each multiplatform library needs to be distributed as a set of artifacts - a common .jar containing the metadata for common code, as well as platform specific .jars containing the compiled implementation code for each platform.
As of Kotlin 1.2, multiplatform projects have to be built with Gradle; other build systems are not supported.
To create a new multiplatform project in the IDE, select the "Kotlin (Multiplatform)" option under "Kotlin" in the New Project dialog. This will create a project with three modules, a common one and two platform ones for JVM and JS. To add additional modules, select one of the "Kotlin (Multiplatform)" options under "Gradle" in the New Module dialog.
If you need to configure the project manually, use the following steps:
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
kotlin-platform-common
plugin to the common modulekotlin-stdlib-common
dependency to the common modulekotlin-platform-jvm
, kotlin-platform-android
, and kotlin-platform-js
plugins to the platform modules for JVM, Android, and JS, respectivelyexpectedBy
scope from the platform modules to the common moduleThe following example demonstrates a complete build.gradle
file for a common module with Kotlin 1.2-Beta:
buildscript { ext.kotlin_version = '1.2.41' repositories { mavenCentral() } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } apply plugin: 'kotlin-platform-common' repositories { mavenCentral() } dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version" testCompile "org.jetbrains.kotlin:kotlin-test-common:$kotlin_version" }
And the example below shows a complete build.gradle
for a JVM module. Pay special attention to the expectedBy
line in the dependencies
block:
buildscript { ext.kotlin_version = '1.2.41' repositories { mavenCentral() } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } apply plugin: 'kotlin-platform-jvm' repositories { mavenCentral() } dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" expectedBy project(":") testCompile "junit:junit:4.12" testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version" testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" }
One of the key capabilities of Kotlin's multiplatform code is a way for common code to depend on platform-specific declarations. In other languages, this can often be accomplished by building a set of interfaces in the common code and implementing these interfaces in platform-specific modules. However, this approach is not ideal in cases when you have a library on one of the platforms that implements the functionality you need, and you'd like to use the API of this library directly without extra wrappers. Also, it requires common declarations to be expressed as interfaces, which doesn't cover all possible cases.
As an alternative, Kotlin provides a mechanism of expected and actual declarations. With this mechanism, a common module can define expected declarations, and a platform module can provide actual declarations corresponding to the expected ones. To see how this works, let's look at an example first. This code is part of a common module:
package org.jetbrains.foo expect class Foo(bar: String) { fun frob() } fun main(args: Array<String>) { Foo("Hello").frob() }
And this is the corresponding JVM module:
package org.jetbrains.foo actual class Foo actual constructor(val bar: String) { actual fun frob() { println("Frobbing the $bar") } }
This illustrates several important points:
expect
keyword; the actual declaration is marked with the actual
keyword.actual
.Note that expected declarations are not restricted to interfaces and interface members. In this example, the expected class has a constructor and can be created directly from common code. You can apply the expect
modifier to other declarations as well, including top-level declarations and annotations:
// Common expect fun formatString(source: String, vararg args: Any): String expect annotation class Test // JVM actual fun formatString(source: String, vararg args: Any) = String.format(source, args) actual typealias Test = org.junit.Test
The compiler ensures that every expected declaration has actual declarations in all platform modules that implement the corresponding common module, and reports an error if any actual declarations are missing. The IDE provides tools that help you create the missing actual declarations.
If you have a platform-specific library that you want to use in common code while providing your own implementation for another platform, you can provide a typealias to an existing class as the actual declaration:
expect class AtomicRef<V>(value: V) { fun get(): V fun set(value: V) fun getAndSet(value: V): V fun compareAndSet(expect: V, update: V): Boolean } actual typealias AtomicRef<V> = java.util.concurrent.atomic.AtomicReference<V>
It is possible to write tests in a common project so that they will be compiled and run in each platform project. There are 4 annotations provided in kotlin.test
package to markup tests in common code: @Test
, @Ignore
, @BeforeTest
and @AfterTest
. In JVM platform these annotations are mapped to the corresponding JUnit 4 annotations, and in JS they are already available since 1.1.4 to support JS unit testing.
In order to use them you need to add a dependency on kotlin-test-annotations-common
to your common module, on kotlin-test-junit
to your JVM module, and on kotlin-test-js
to the JS module.
© 2010–2018 JetBrains s.r.o.
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/reference/multiplatform.html