Kotlin: Write Once, Run Actually Everywhere (QCon SF 2018)

E68309f117985270285ade8082f4877d?s=47 Jake Wharton
November 06, 2018

Kotlin: Write Once, Run Actually Everywhere (QCon SF 2018)

Kotlin used to be described as a new language for the JVM which aims to fix some of the pain points of Java. But more recently Kotlin is also a language for the web, iOS, desktop, embedded, and just about anywhere else code can run. JetBrains, the creators of Kotlin, have placed a strong emphasis on targeting multiple platforms in how the language is compiled, its language features, and the standard libraries. This talk will be an exploration of the Kotlin language, how it compiles to run on more than just the JVM, and whether it can fully pull off the multiplatform trick allowing a single codebase to run everywhere.

Video: coming soon

E68309f117985270285ade8082f4877d?s=128

Jake Wharton

November 06, 2018
Tweet

Transcript

  1. Jake Wharton Kotlin: Write Once, Run Everywhere Actually

  2. Kotlin?

  3. None
  4. None
  5. None
  6. None
  7. None
  8. val firstName: String = "Jake"
 val lastName: String? = null

  9. val firstName: String = "Jake"
 val lastName: String? = null

  10. val firstName: String = "Jake"
 val lastName: String? = null

  11. val firstName: String = "Jake"
 val lastName: String? = null

  12. val firstName: String = "Jake"
 val lastName: String? = null

  13. val firstName = "Jake"
 val lastName: String? = null

  14. class User {
 public String getName() {
 // ...
 }


    public void setName(String name) {
 // ...
 }
 } // ^^^ Java
  15. class User {
 public String getName() {
 // ...
 }X


    public void setName(String name) {
 // ...
 }Y
 }Z // ^^^ Java vvv Kotlin val user = User()
 println("Name is " + user.name)
  16. class User {
 public String getName() {
 // ...
 }X


    public void setName(String name) {
 // ...
 }Y
 }Z // ^^^ Java vvv Kotlin val user = User()
 println("Name is " + user.name)X
  17. class User {
 public String getName() {
 // ...
 }X


    public void setName(String name) {
 // ...
 }Y
 }Z // ^^^ Java vvv Kotlin val user = User()
 println("Name is ${user.name}")X
  18. class User {
 public String getName() {
 // ...
 }X


    public void setName(String name) {
 // ...
 }Y
 }Z // ^^^ Java vvv Kotlin val user = User()
 println("Name is $user")X
  19. class User {
 public String getName() {
 // ...
 }X


    public void setName(String name) {
 // ...
 }Y
 }Z // ^^^ Java vvv Kotlin val user = User()
 println("Name is $user")X
  20. class User {
 var name = "Jake"
 } // ^^^

    Kotlin
  21. class User {
 var name = "Jake"
 } // ^^^

    Kotlin vvv Java User user = new User(); System.out.println("Name is " + user.getName());
  22. class User {
 var name = "Jake"
 } // ^^^

    Kotlin vvv Java User user = new User(); System.out.println("Name is " + user.getName());
  23. class User {
 var name = "Jake"
 } // ^^^

    Kotlin vvv Java User user = new User(); System.out.println("Name is " + user.getName()); user.setName("Jane");
  24. class User {
 var name = "Jake"
 } // ^^^

    Kotlin vvv Java User user = new User(); System.out.println("Name is " + user.getName()); user.setName("Jane");
  25. val user = User()

  26. val user = User()
 user = User()

  27. val user = User()
 user = User()
 
 var currentUser

    = User()
 currentUser = User()
  28. fun Date.isTuesday(): Boolean {
 return day == 2
 }

  29. fun Date.isTuesday(): Boolean {
 return day == 2
 } val

    epoch = Date(1970, 0, 0)
 if (epoch.isTuesday()) {
 println("The epoch was a Tuesday.")
 } else {
 println("The epoch was not a Tuesday.")
 }
  30. fun Date.isTuesday(): Boolean {
 return day == 2
 } val

    epoch = Date(1970, 0, 0)
 if (epoch.isTuesday()) {
 println("The epoch was a Tuesday.")
 } else {
 println("The epoch was not a Tuesday.")
 }
  31. fun Date.isTuesday(): Boolean {
 return day == 2
 } val

    epoch = Date(1970, 0, 0)
 if (epoch.isTuesday()) {
 println("The epoch was a Tuesday.")
 } else {
 println("The epoch was not a Tuesday.")
 } // ^^^ Kotlin vvv Java DateKt.isTuesday(date)
  32. val executor = Executors.newSingleThreadExecutor();
 executor.execute {Bprintln("Background thread!") }X

  33. val executor = Executors.newSingleThreadExecutor(); val foo = Foo()
 executor.execute(foo::printIt) class

    Foo { fun printIt() {B println("Background thread!") }X }
  34. val executor = Executors.newSingleThreadExecutor(); val foo = Foo()
 executor.execute(foo::printIt) class

    Foo { fun printIt() {B println("Background thread!") }X }
  35. fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T> {
 // ...


    }
  36. fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T> {
 // ...


    }
  37. fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T> {
 // ...


    }A val items = listOf(1, 2, 3) val odds = items.filter({ item -> item % 2 != 0 })B
  38. fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T> {
 // ...


    }A val items = listOf(1, 2, 3) val odds = items.filter({ item -> item % 2 != 0 })B
  39. fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T> {
 // ...


    }A val items = listOf(1, 2, 3) val odds = items.filter({ it % 2 != 0 })B
  40. fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T> {
 // ...


    }A val items = listOf(1, 2, 3) val odds = items.filter()B{ it % 2 != 0 }
  41. fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T> {
 // ...


    }A val items = listOf(1, 2, 3) val odds = items.filter { it % 2 != 0 }
  42. 
 
 val odds fun <T> List<T>.filter(predicate: (T) -> Boolean):

    List<T> {
 // ...
 }A val items = listOf(1, 2, 3) List = items.filter { it % 2 != 0 } val oddSet = items.filterTo(mutableListOf()) { it % 2 != 0 }
  43. fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T> {
 // ...


    }A val items = listOf(1, 2, 3) val odds = items.filter { it % 2 != 0 }
  44. inline fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T> {
 //

    ...
 }A val items = listOf(1, 2, 3) val odds = items.filter { it % 2 != 0 }
  45. inline fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T> { val

    destination = mutableListOf<T>() for (item in this) { if (predicate(item)) destination.add(item) }B return destination }A val items = listOf(1, 2, 3) val odds = items.filter { it % 2 != 0 } val destination = mutableListOf< >() for (item in ) { if ( item ) destination.add(item) }G destination
  46. inline fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T> { val

    destination = mutableListOf<T>() for (item in this) { if (predicate(item)) destination.add(item) }B return destination }A val items = listOf(1, 2, 3) val destination = mutableListOf<Int>() for (item in items) { if (item % 2 != 0) destination.add(item) }G val odds = destination filter it
  47. class User {D
 val name = "Jake"
 }A

  48. class User(name: String) {D
 val name = name
 }A 


    "Jake"
  49. class User(val name: String) {
 }A

  50. class User(val name: String)

  51. class User(val name: String) val jake = User("Jake") println("Hello, $jake!")

  52. class User(val name: String) val jake = User("Jake") println("Hello, $jake!")

    Hello, User@3a71f4dd!
  53. data class User(val name: String) val jake = User("Jake") println("Hello,

    $jake!") Hello, User@3a71f4dd!
  54. @3a71f4dd data class User(val name: String) val jake = User("Jake")

    println("Hello, $jake!") Hello, User(name=Jake)!
  55. data class User(val name: String) val jake = User("Jake") println("Hello,

    $jake!") Hello, User(name=Jake)!
  56. data class User(val name: String, val age: Int) val jake

    = User("Jake") println("Hello, $jake!") Hello, User(name=Jake)!
  57. data class User(val name: String, val age: Int) val jake

    = User("Jake", 4)
  58. data class User(val name: String, val age: Int) val jake

    = User("Jake", 4) val (name, age) = jake
  59. data class User(val name: String, val age: Int) val (name,

    age) = User("Jake", 4)
  60. data class User(val name: String, val age: Int) fun dbLookup()

    = User("Jake", 4) val (name, age) = dbLookup()
  61. class UserPersisence(db: SqlDatabase) { private val deleteByName = db.createStatement("DELETE FROM

    user WHERE name = ?") fun delete(name: String) { deleteByName.bind(1, name) deleteByName.execute() } }
  62. class UserPersisence(db: SqlDatabase) { private val deleteByName = db.createStatement("DELETE FROM

    user WHERE name = ?") fun delete(name: String) { deleteByName.bind(1, name) deleteByName.execute() }B }A
  63. class UserPersisence(db: SqlDatabase) { private val deleteByName by lazy {

    db.createStatement("DELETE FROM user WHERE name = ?") }C fun delete(name: String) { deleteByName.bind(1, name) deleteByName.execute() }B }A
  64. val deleteByName by lazy { db.createStatement("DELETE FROM user WHERE name

    = ?") }C
  65. val deleteByName by lazy { db.createStatement("DELETE FROM user WHERE name

    = ?") }C var name by Delegates.observable("Jane") { prop, old, new -> println("Name changed from $old to $new") }
  66. val deleteByName by lazy { db.createStatement("DELETE FROM user WHERE name

    = ?") }C var name by Delegates.observable("Jane") { prop, old, new -> println("Name changed from $old to $new") } var address by Delegates.notNull<String>()
  67. val deleteByName by lazy { db.createStatement("DELETE FROM user WHERE name

    = ?") }C var name by Delegates.observable("Jane") { prop, old, new -> println("Name changed from $old to $new") } var address by Delegates.notNull<String>() val nameView by bindView<TextView>(R.id.name)
  68. val deleteByName by lazy { db.createStatement("DELETE FROM user WHERE name

    = ?") }C var name by Delegates.observable("Jane") { prop, old, new -> println("Name changed from $old to $new") } var address by Delegates.notNull<String>() val nameView by bindView<TextView>(R.id.name)
  69. fun main(vararg args: String) = runBlocking<Unit> { val jobs =

    List(100_000) { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } }
  70. None
  71. None
  72. None
  73. Multiplatform Abstraction Your App

  74. Your App

  75. Your App

  76. X O X X X O O O X

  77. iOS Web Server / API Android

  78. Android

  79. package xo; public enum Mark { X, O; }

  80. package xo; import java.util.Arrays; public final class Board { private

    static final int SIZE = 3; private final Mark[][] cells; public Board() { this.cells = new Mark[3][3]; } // TODO mutator methods... @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Board)) return false; Board other = (Board) o; return Arrays.deepEquals(cells, other.cells); } @Override public int hashCode() { return Arrays.deepHashCode(cells);
  81. package xo; import static java.util.Objects.requireNonNull; public final class Player {

    public final String name; public final Mark mark; public Player(String name, Mark mark) { this.name = requireNonNull(name, "name == null"); this.mark = requireNonNull(mark, "mark == null"); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Player)) return false; Player other = (Player) o; return name.equals(other.name) && mark == other.mark; } @Override public int hashCode() { return 31 * name.hashCode() + mark.hashCode(); }
  82. package xo; public enum State { PLAYER_1_MOVE, PLAYER_2_MOVE, PLAYER_1_WIN, PLAYER_2_WIN,

    DRAW, }
  83. package xo; import static java.util.Objects.requireNonNull; public final class Game {

    private final Board board; private final Player player1; private final Player player2; private State state = State.PLAYER_1_MOVE; public Game(Board board, Player player1, Player player2) { this.board = requireNonNull(board, "board == null"); this.player1 = requireNonNull(player1, "player1 == null"); this.player2 = requireNonNull(player2, "player2 == null"); } // TODO mutator methods... @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Game)) return false; Game other = (Game) o; return board.equals(other.board) && player1.equals(other.player1)
  84. Game Board Mark State Player

  85. Game Board Mark State Player

  86. public final class Player { public final String name; public

    final Mark mark; public Player(String name, Mark mark) { this.name = requireNonNull(name, "name == null"); this.mark = requireNonNull(mark, "mark == null"); }A @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Player)) return false; Player other = (Player) o; return name.equals(other.name) && mark == other.mark; }B @Override public int hashCode() { return 31 * name.hashCode() + mark.hashCode(); }C @Override public String toString() { return "Player{name='" + name + ", mark=" + mark + '}'; }D }E
  87. data class Player(val name: String, val mark: Mark) public final

    { public final ; public final ; public Player(String name, Mark mark) { this.name = requireNonNull(name, "name == null"); this.mark = requireNonNull(mark, "mark == null"); }A @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Player)) return false; Player other = (Player) o; return name.equals(other.name) && mark == other.mark; }B @Override public int hashCode() { return 31 * name.hashCode() + mark.hashCode(); }C @Override public String toString() { return "Player{name='" + name + ", mark=" + mark + '}'; }D }E
  88. Game Mark State Board Player

  89. State Board Game Mark Player

  90. Game Mark State Board *.java *.kt Player

  91. *.kt Game Mark State *.java

  92. kotlinc *.kt Game Mark State *.java

  93. *.kt Game Mark State *.java kotlinc

  94. javac *.kt Game Mark State *.java kotlinc

  95. kotlinc javac *.kt Game Mark State *.java

  96. kotlinc javac *.kt Game Mark State *.java

  97. kotlin-stdlib kotlinc javac *.kt Game Mark State *.java

  98. kotlin-stdlib kotlinc javac *.kt Game Mark State *.java models/

  99. iOS Web Server / API Android

  100. iOS Web Server / API Android

  101. iOS Web Server / API Android View Models

  102. iOS Web Server / API Android View Models Presenters

  103. iOS Web Server / API Android View Models Presenters

  104. iOS Web Server / API Android View Models Presenters Client

    Backend
  105. iOS Web Server / API Android View Models Presenters Client

    Backend
  106. iOS Web Server / API Android View Models Presenters Client

    Backend Business Logic
  107. iOS Web Server / API Android View Models Presenters Client

    Backend Business Logic Models
  108. iOS Web Server / API Android View Models Presenters Client

    Backend Business Logic Models
  109. data class NewGameUiModel( val winTotal: Long, val lossTotal: Long )

  110. data class NewGameUiModel( val winTotal: Long, val lossTotal: Long )

    data class GameUiModel( val game: Game )
  111. data class NewGameUiModel( val winTotal: Long, val lossTotal: Long )

    data class GameUiModel( ) val game: Game
  112. data class NewGameUiModel( val winTotal: Long, val lossTotal: Long )

    data class GameUiModel( val game: Game )
  113. iOS Web Server / API Android View Models Presenters Client

    Backend Business Logic Models
  114. iOS Web Server / API Android View Models Presenters Client

    Backend Business Logic Models
  115. iOS Web Server / API Android View Models Presenters Client

    Backend Business Logic Models
  116. class NewGamePresenter {C fun model(): NewGameUiModel { }B }A

  117. class NewGamePresenter(private val gameStore: GameStore) {C fun model(): NewGameUiModel {

    }B }A
  118. class NewGamePresenter(private val gameStore: GameStore) {C fun model(): NewGameUiModel {

    val totals = gameStore.totals() return NewGameUiModel(totals.wins, totals.losses) }B }A
  119. class GamePresenter {D fun model(): GameUiModel {C }B }A

  120. class GamePresenter(private val gameId: Long) {D fun model(): GameUiModel {C

    }B }A
  121. class GamePresenter( private val gameId: Long, private val gameStore: GameStore

    ) {D fun model(): GameUiModel {C }B }A
  122. class GamePresenter( private val gameId: Long, private val gameStore: GameStore

    ) {D fun models(): Observable<GameUiModel> {C }B }A
  123. class GamePresenter( private val gameId: Long, private val gameStore: GameStore

    ) {D fun move(row: Int, col: Int )Z{C }G fun models(): Observable<GameUiModel> {C }B }A
  124. class GamePresenter( private val gameId: Long, private val gameStore: GameStore

    ) {D fun models(events: Observable<UiEvent>): Observable<GameUiModel> {C }B sealed class UiEvent { data class Move(val row: Int, val col: Int): UiEvent() // ... } }A fun move( )Z{C }G
  125. iOS Web Server / API Android View Models Presenters Client

    Backend Business Logic Models
  126. iOS Web Server / API Android View Models Presenters Client

    Backend Business Logic Models
  127. iOS Web Server / API Android View Models Presenters Client

    Backend Business Logic Models
  128. iOS Web Server / API Android View Models Presenters Client

    Backend Business Logic Models
  129. interface GameStore { }A

  130. interface GameStore { suspend fun totals(): Totals data class Totals(val

    wins: Long, val losses: Long) }A
  131. interface GameStore { suspend fun totals(): Totals fun game(id: Long):

    Observable<Game> data class Totals(val wins: Long, val losses: Long) }A
  132. interface GameStore { suspend fun totals(): Totals fun game(id: Long):

    Observable<Game> suspend fun move(id: Long, row: Int, col: Int) data class Totals(val wins: Long, val losses: Long) }A
  133. iOS Web Server / API Android View Models Presenters Client

    Backend Business Logic Models
  134. iOS Web Server / API Android View Models Presenters Client

    Backend Business Logic Models
  135. Android iOS Web iOS Web Server / API Android View

    Models Presenters Client Backend Business Logic Models
  136. class SqliteGameStore(private val db: SQLiteDatabase) : GameStore { override suspend

    fun totals() = TODO() override fun game(id: Long) = TODO() override suspend fun move(id: Long, row: Int, col: Int) = TODO() }
  137. class IosGameStore(private val db: CoreDataGameStore) : GameStore { override suspend

    fun totals() = TODO() override fun game(id: Long) = TODO() override suspend fun move(id: Long, row: Int, col: Int) = TODO() }
  138. class IosGameStore( ) : GameStore { override suspend fun totals()

    = TODO() override fun game(id: Long) = TODO() override suspend fun move(id: Long, row: Int, col: Int) = TODO() } private val db: CoreDataGameStore
  139. class IosGameStore( ) : GameStore { override suspend fun totals()

    = TODO() override fun game(id: Long) = TODO() override suspend fun move(id: Long, row: Int, col: Int) = TODO() } private val db: CoreDataGameStore // tictactoe.def headers = game_store.h
  140. class StorageGameStore(private val store: Storage) : GameStore { override suspend

    fun totals() = TODO() override fun game(id: Long) = TODO() override suspend fun move(id: Long, row: Int, col: Int) = TODO() }A
  141. import org.w3c.dom.Storage class StorageGameStore( ) : GameStore { override suspend

    fun totals() = TODO() override fun game(id: Long) = TODO() override suspend fun move(id: Long, row: Int, col: Int) = TODO() }A private val store: Storage
  142. Android iOS Web iOS Web Server / API Android View

    Models Presenters Client Backend Business Logic Models
  143. Android iOS Web iOS Web Server / API Android View

    Models Presenters Client Backend Business Logic Models
  144. Android iOS Web iOS Web Server / API Android View

    Models Presenters Client Backend Business Logic Models
  145. Android iOS Web iOS Web Server / API Android View

    Models Presenters Client Backend Business Logic Models
  146. object TicTacToeLogic { fun validateMove( game: Game, player: Player, row:

    Int, col: Int): Boolean { when (game.state) { State.PLAYER_1_MOVE -> require(game.player1 == player) State.PLAYER_2_MOVE -> require(game.player2 == player) else -> error("Game is over") } return game.board[row][col] == null } fun nextState(game: Game): State { findWinner(game.board)?.let { return if (game.player1.mark == it) State.PLAYER_1_WIN else State.PLAYER_2_WIN } if (game.board.isComplete()) { return State.DRAW } return if (game.state == State.PLAYER_1_MOVE) State.PLAYER_2_MOVE else State.PLAYER_1_MOVE } fun findWinner(board: Board): Mark? = TODO() fun Board.isComplete(): Boolean = TODO() }
  147. Android iOS Web iOS Web Server / API Android View

    Models Presenters Client Backend Business Logic Models
  148. Android iOS Web iOS Web Server / API Android View

    Models Presenters Client Backend Business Logic Models
  149. Android iOS Web iOS Web Server / API Android View

    Models Presenters Client Backend Business Logic Models
  150. Android iOS Web iOS Web Server / API Android View

    Models Presenters Client Backend Business Logic Models
  151. class GameView(context: Context, attrs: AttributeSet) : ContraintLayout(context, attrs), Consumer<GamePresenter.UiModel> {

    override fun accept(model: GamePresenter.UiModel) { // TODO bind to view... } }
  152. class GameViewController : UiViewController { func update(model: GamePresenter.UiModel) { //

    TODO bind to view... } }
  153. function update(model) { // TODO bind to DOM/template/JSX/whatever... }

  154. @POST @Path("/api/move") fun Game move( @QueryParam("id") id: Long, @QueryParam("row") row:

    Int, @QueryParam("col") col: Int) { // TODO check business logic, persist, return updated game ... }
  155. None
  156. apk via aab apk via aab apk webapk apk via

    aab via url url apk via aab via url Chrome app url url pwa ipa ipa url url
  157. None
  158. None
  159. kotlin-stdlib kotlinc javac *.kt *.java

  160. None
  161. None
  162. external interface ChromePlatform { val omnibox: Omnibox val storage: Storage

    val tabs: Tabs }A @JsName("chrome") external val Chrome: ChromePlatform
  163. $ ts2kt chrome.ts external interface ChromePlatform { val omnibox: Omnibox

    val storage: Storage val tabs: Tabs }A @JsName("chrome") external val Chrome: ChromePlatform
  164. external interface ChromePlatform { val omnibox: Omnibox val storage: Storage

    val tabs: Tabs }A @JsName("chrome") external val Chrome: ChromePlatform
  165. external interface ChromePlatform { val omnibox: Omnibox val storage: Storage

    val tabs: Tabs }A @JsName("chrome") external val Chrome: ChromePlatform fun main(vararg args: String) { val configStore = StorageAreaConfigStore( Chrome.storage.sync, PRODUCTION_GIT_WEB, PRODUCTION_DAC) // ... }
  166. function main(args) { var configStore = new StorageAreaConfigStore(chrome.storage.sync, reference.PRODUCTION_GIT_WEB, reference.PRODUCTION_DAC);

    // ... }
  167. None
  168. None
  169. class IosGameStore(private val db: CoreDataGameStore) : GameStore { override suspend

    fun totals() = TODO() override fun game(id: Long) = TODO() override suspend fun move(id: Long, row: Int, col: Int) = TODO() }
  170. class IosGameStore(private val db: CoreDataGameStore) : GameStore { override suspend

    fun totals() = TODO() override fun game(id: Long) = TODO() override suspend fun move(id: Long, row: Int, col: Int) = TODO() }
  171. class IosGameStore(private val db: CoreDataGameStore) : GameStore { override suspend

    fun totals() = TODO() override fun game(id: Long) = TODO() override suspend fun move(id: Long, row: Int, col: Int) = TODO() } // tictactoe.def headers = game_store.h
  172. class IosGameStore(private val db: CoreDataGameStore) : GameStore { override suspend

    fun totals() = TODO() override fun game(id: Long) = TODO() override suspend fun move(id: Long, row: Int, col: Int) = TODO() }A // tictactoe.def headers = game_store.h package = gamestore
  173. import gamestore.CoreDataGameStore class IosGameStore(private val db: CoreDataGameStore) : GameStore {

    override suspend fun totals() = TODO() override fun game(id: Long) = TODO() override suspend fun move(id: Long, row: Int, col: Int) = TODO() }A // tictactoe.def headers = game_store.h package = gamestore
  174. konan *.kt

  175. konan *.kt *.h

  176. konan *.kt llvm *.h

  177. konan *.kt llvm *.h

  178. konan *.kt llvm *.h

  179. None
  180. None
  181. import kotlin.collections.ArrayList import kotlin.collections.MutableList val users: MutableList<String> = ArrayList()

  182. import java.util.concurrent.CopyOnWriteList import kotlin.collections.MutableList val users: MutableList<String> = CopyOnWriteArrayList() kotlin.collections.ArrayList

  183. import kotlin.collections.MutableList val users: MutableList<String> = CopyOnWriteArrayList()

  184. import kotlin.collections.MutableList val users: MutableList<String> = CopyOnWriteArrayList() expect class CopyOnWriteArrayList<T>

    : MutableList<T>
  185. import kotlin.collections.MutableList val users: MutableList<String> = CopyOnWriteArrayList() expect class CopyOnWriteArrayList<T>

    : MutableList<T> actual typealias CopyOnWriteArrayList<T> = java.util.concurrent.CopyOnWriteArrayList<T>
  186. import kotlin.collections.MutableList val users: MutableList<String> = CopyOnWriteArrayList() expect class CopyOnWriteArrayList<T>

    : MutableList<T> actual typealias CopyOnWriteArrayList<T> = java.util.concurrent.CopyOnWriteArrayList<T> actual typealias CopyOnWriteArrayList<T> = ArrayList<T>
  187. import kotlin.collections.MutableList val users: MutableList<String> = CopyOnWriteArrayList() expect class CopyOnWriteArrayList<T>

    : MutableList<T> actual typealias CopyOnWriteArrayList<T> = java.util.concurrent.CopyOnWriteArrayList<T> actual typealias CopyOnWriteArrayList<T> = ArrayList<T> actual class CopyOnWriteArrayList<T> : MutableList<T> { // it's complicated... }
  188. expect interface Closeable { fun close() }

  189. expect interface Closeable { fun close() } actual typealias Closeable

    = java.io.Closeable actual interface Closeable { actual fun close() } actual interface Closeable { actual fun close() }
  190. None
  191. None
  192. SDK Search

  193. None
  194. None
  195. None
  196. None
  197. @JakeWharton Kotlin: Write Once, Run Everywhere Actually