コードで見るKotlin 1.3

E3575b2139d781a15136d253bb8d7ba9?s=47 Kohei Yamato
November 07, 2018

コードで見るKotlin 1.3

E3575b2139d781a15136d253bb8d7ba9?s=128

Kohei Yamato

November 07, 2018
Tweet

Transcript

  1. コードで見る Kotlin 1.3 どこでもKotlin #6 エムスリー株式会社 大和康平

  2. 自己紹介 • 大和 康平 (24) • エムスリー 2018年4月入社 • 仕事の担当:

    サーバーサイド • 趣味: モータースポーツ観戦
  3. 発表の概要 • 基本的にはこちらの内容 ◦ What's New in Kotlin 1.3 (https://kotl.in/1.3)

    • この中から3つをピックアップして紹介 ◦ Coroutines (ごく一部) ◦ Inline Classes ◦ Standard Libraries
  4. Kotlin 1.3 リリース

  5. Kotlin 1.3 事始め コマンドラインの場合: Gradleの場合: $ brew install kotlin plugins

    { id 'org.jetbrains.kotlin.jvm' version '1.3.0' }
  6. Coroutines

  7. Coroutines • 基本は軽量なスレッド • CoroutineScope 中の coroutine builder により起動する ◦

    launch ◦ runBlocking ◦ async
  8. Coroutines dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.0' } coroutine の実行に必要

  9. GrobalScope fun main() { GlobalScope.launch { delay(1000L) println("World!") } println("Hello,")

    Thread.sleep(2000L) }
  10. GrobalScope fun main() { GlobalScope.launch { delay(1000L) println("World!") } println("Hello,")

    Thread.sleep(2000L) } GlobalScope の coroutine
  11. GrobalScope • CoroutineScope の一種 • ライフタイム: アプリケーション終了時まで

  12. launch coroutine builder • 現在のスレッドをブロックせずに、新しい coroutine を起動 する • Coroutine

    への参照を Job として返す (後述)
  13. launch coroutine builder fun main() { GlobalScope.launch { delay(1000L) println("World!")

    } println("Hello,") Thread.sleep(2000L) }
  14. launch coroutine builder fun main() { GlobalScope.launch { delay(1000L) println("World!")

    } println("Hello,") Thread.sleep(2000L) } メインスレッドをブロックしない が1000ms停止
  15. launch coroutine builder fun main() { GlobalScope.launch { delay(1000L) println("World!")

    } println("Hello,") Thread.sleep(2000L) } 先に実行される
  16. launch coroutine builder fun main() { GlobalScope.launch { delay(1000L) println("World!")

    } println("Hello,") Thread.sleep(2000L) } => Hello, World!
  17. launch coroutine builder fun main() { GlobalScope.launch { delay(1000L) println("World!")

    } println("Hello,") Thread.sleep(2000L) } => Hello, World! 2000ms停止することで待 ち合わせ
  18. launch coroutine builder fun main() { GlobalScope.launch { delay(1000L) println("World!")

    } println("Hello,") } => Hello, 実行される前に終了
  19. Job fun main() { GlobalScope.launch { delay(1000L) println("World!") } println("Hello,")

    Thread.sleep(2000L) } coroutineの内容によらず 2000ms停止......
  20. Job fun main() { val job = GlobalScope.launch { delay(1000L)

    println("World!") } println("Hello,") job.join() }
  21. Job • launch coroutine builder の返り値 • いくつかの状態を持つ ◦ New,

    Active, Completing, Cancelling, Cancelled, Completed • Job#join を実行することにより、完了するまで呼び出し元 の coroutine を停止できる
  22. Job fun main() { val job = GlobalScope.launch { delay(1000L)

    println("World!") } println("Hello,") job.join() } coroutineが完了するまで待ち合わせ
  23. Job fun main() { val job = GlobalScope.launch { delay(1000L)

    println("World!") } println("Hello,") job.join() } => Hello, World!
  24. runBlocking coroutine builder • 現在のスレッドをブロックして、新しい coroutine を起動する • コードブロックの返り値をそのまま返す

  25. runBlocking coroutine builder fun main() { val job = GlobalScope.launch

    { delay(1000L) println("World!") } println("Hello,") job.join() }
  26. runBlocking coroutine builder fun main() { runBlocking { delay(1000L) println("World!")

    } println("Hello,") }
  27. runBlocking coroutine builder fun main() { runBlocking { delay(1000L) println("World!")

    } println("Hello,") } 完了するまでメインスレッドをブ ロック
  28. runBlocking coroutine builder fun main() { runBlocking { delay(1000L) println("World!")

    } println("Hello,") } => World! (約1秒後) Hello,
  29. CoroutineScope fun main() { val job = GlobalScope.launch { delay(1000L)

    println("World!") } println("Hello,") job.join() }
  30. CoroutineScope fun main() = runBlocking { val job = GlobalScope.launch

    { delay(1000L) println("World!") } println("Hello,") job.join() }
  31. CoroutineScope fun main() = runBlocking { launch { delay(1000L) println("World!")

    } println("Hello,") }
  32. CoroutineScope fun main() = runBlocking { // this: CoroutineScope launch

    { delay(1000L) println("World!") } println("Hello,") }
  33. CoroutineScope • 全ての coroutine builder は、そのコードブロックのスコープ に CroutineScope のインスタンスを追加する •

    外のコルーチンは、スコープ中で起動されたコルーチンが全 て終了するまで完了しない
  34. CoroutineScope fun main() = runBlocking { launch { delay(1000L) println("World!")

    } println("Hello,") } => Hello, World! coroutine が完了するまで アプリケーションは終了しない
  35. async coroutine builder • 現在のスレッドをブロックせずに、新しい coroutine を起動 する ◦ launch

    と同じ • 将来の実行結果を Deferred として返す
  36. async coroutine builder fun main() = runBlocking { val x

    = async { delay(1000L) 10 } println(x.await()) }
  37. async coroutine builder fun main() = runBlocking { val x

    = async { delay(1000L) 10 } println(x.await()) } async による coroutine
  38. async coroutine builder fun main() = runBlocking { val x

    = async { delay(1000L) 10 } println(x.await()) } coroutine の完了まで待つ
  39. async coroutine builder fun main() = runBlocking { val x

    = async { delay(1000L) 10 } println(x.await()) } => 10 (約1秒後)
  40. async coroutine builder fun main() = runBlocking { val x

    = async { delay(1000L); 10 } val y = async { delay(2000L); 20 } println(x.await() + y.await()) }
  41. async coroutine builder fun main() = runBlocking { val x

    = async { delay(1000L); 10 } val y = async { delay(2000L); 20 } println(x.await() + y.await()) }
  42. async coroutine builder fun main() = runBlocking { val x

    = async { delay(1000L); 10 } val y = async { delay(2000L); 20 } println(x.await() + y.await()) }
  43. async coroutine builder fun main() = runBlocking { val x

    = async { delay(1000L); 10 } val y = async { delay(2000L); 20 } println(x.await() + y.await()) } 最大2000ms停止
  44. async coroutine builder fun main() = runBlocking { val x

    = async { delay(1000L); 10 } val y = async { delay(2000L); 20 } println(x.await() + y.await()) } => 30 (約2秒後)
  45. async coroutine builder fun main() = runBlocking { val time

    = measureTimeMillis { val x = async { delay(1000L); 10 } val y = async { delay(2000L); 20 } println(x.await() + y.await()) } println("$time ms") } => 30 2033 ms 時間計測しても約2000ms
  46. Coroutines まとめ • Coroutine builder を使用して作成する ◦ launch ◦ runBlocking

    ◦ async • 階層構造を持たせて実行するとよい
  47. Inline Classes [experimental]

  48. Inline Classes • コンパイル時にオーバーヘッドが少ない形で展開されるクラ ス • プライマリコンストラクタで、プロパティを1つのみ宣言できる

  49. プライマリコンストラクタのみ inline class Password(val value: String) fun main() { val

    securePassword = Password("Don't try this in production") println(securePassword.value) }
  50. プライマリコンストラクタのみ inline class Password(val value: String) fun main() { val

    securePassword = Password("Don't try this in production") println(securePassword.value) } プロパティを1つ持つ Inline Class
  51. プライマリコンストラクタのみ inline class Password(val value: String) fun main() { val

    securePassword = Password("Don't try this in production") println(securePassword.value) } 通常の Class として利用できる
  52. プライマリコンストラクタのみ (decompiled) public static final void main() { String securePassword

    = Password.constructor-impl("Don't try this in production"); System.out.println(securePassword); } Java に逆コンパイルした main 関数
  53. プライマリコンストラクタのみ (decompiled) public static final void main() { String securePassword

    = Password.constructor-impl("Don't try this in production"); System.out.println(securePassword); } プロパティの型が返る
  54. プライマリコンストラクタのみ (decompiled) public static final void main() { String securePassword

    = Password.constructor-impl("Don't try this in production"); System.out.println(securePassword); } static method として実行される
  55. プライマリコンストラクタのみ (decompiled) public final class Password { @NotNull public static

    String constructor_impl/* $FF was: constructor-impl*/(@NotNull String value) { Intrinsics.checkParameterIsNotNull(value, "value"); return value; } } 逆コンパイルした Inline class
  56. プライマリコンストラクタのみ (decompiled) public final class Password { @NotNull public static

    String constructor_impl/* $FF was: constructor-impl*/(@NotNull String value) { Intrinsics.checkParameterIsNotNull(value, "value"); return value; } } 引数をそのまま返す
  57. プロパティ&メソッド inline class Name(val s: String) { val length: Int

    get() = s.length fun greet() { println("Hello, $s") } } fun main() { val name = Name("Kotlin") name.greet() println(name.length) }
  58. プロパティ&メソッド inline class Name(val s: String) { val length: Int

    get() = s.length fun greet() { println("Hello, $s") } } fun main() { val name = Name("Kotlin") name.greet() println(name.length) } 引数は1つのみ
  59. プロパティ&メソッド inline class Name(val s: String) { val length: Int

    get() = s.length fun greet() { println("Hello, $s") } } fun main() { val name = Name("Kotlin") name.greet() println(name.length) }
  60. プロパティ&メソッド inline class Name(val s: String) { val length: Int

    get() = s.length fun greet() { println("Hello, $s") } } fun main() { val name = Name("Kotlin") name.greet() println(name.length) } それぞれ単純な処理
  61. プロパティ&メソッド (decompiled) public static final void main() { String name

    = Name.constructor-impl("Kotlin"); Name.greet-impl(name); int var1 = Name.getLength-impl(name); System.out.println(var1); }
  62. プロパティ&メソッド (decompiled) public static final void main() { String name

    = Name.constructor-impl("Kotlin"); Name.greet-impl(name); int var1 = Name.getLength-impl(name); System.out.println(var1); } static method で String を返す
  63. プロパティ&メソッド (decompiled) public static final void main() { String name

    = Name.constructor-impl("Kotlin"); Name.greet-impl(name); int var1 = Name.getLength-impl(name); System.out.println(var1); } コンストラクタと同様に static method が呼ばれる
  64. プロパティ&メソッド (decompiled) public final class Name { public static final

    int getLength_impl/* $FF was: getLength-impl*/(String $this) { return $this.length(); } public static final void greet_impl/* $FF was: greet-impl*/(String $this) { String var1 = "Hello, " + $this; System.out.println(var1); } }
  65. プロパティ&メソッド (decompiled) public final class Name { public static final

    int getLength_impl/* $FF was: getLength-impl*/(String $this) { return $this.length(); } public static final void greet_impl/* $FF was: greet-impl*/(String $this) { String var1 = "Hello, " + $this; System.out.println(var1); } } length() を呼び出すだけ
  66. プロパティ&メソッド (decompiled) public final class Name { public static final

    int getLength_impl/* $FF was: getLength-impl*/(String $this) { return $this.length(); } public static final void greet_impl/* $FF was: greet-impl*/(String $this) { String var1 = "Hello, " + $this; System.out.println(var1); } } String を結合して出力
  67. Inline Classes まとめ • コンパイル時に static method として展開される • いくつかの制約がある

    ◦ プライマリコンストラクタのプロパティは1つ ◦ init 文を持てない, class を持てない, etc. • Experimental: 変更される可能性あり
  68. Standard Libraries

  69. Random マルチプラットフォームで利用可能な Random が追加 val number = Random.nextInt(42) println(number) //

    0 <= number < 42
  70. Random マルチプラットフォームで利用可能な Random が追加 val number = Random(111).nextInt(42) println(number) //

    0 <= number < 42 seed も指定可能
  71. Random IntRange 等の拡張関数も追加されている val number = (0..41).random() println(number) // 0

    <= number <= 41
  72. isNullOrEmpty / orEmpty Collection, Map, Array に追加で導入 fun main() {

    val s: String? = null if (!s.isNullOrEmpty()) { println(s.length) } }
  73. isNullOrEmpty / orEmpty Contracts によりスマートキャストできる fun main() { val s:

    String? = null if (!s.isNullOrEmpty()) { println(s.length) } } スマートキャスト
  74. isNullOrEmpty / orEmpty Contracts によりスマートキャストできる @kotlin.internal.InlineOnly public inline fun CharSequence?.isNullOrEmpty():

    Boolean { contract { return(false) implies (this@isNullOrEmpty != null) } return random(Random) }
  75. Array.copyInto 別の Array に要素をコピー可能に val source = "kotlin".toCharArray() val target

    = "java".toCharArray() source.copyInto(target, endIndex = target.size) println(target) // kotl
  76. Array.copyInto 別の Array に要素をコピー可能に val source = "kotlin".toCharArray() val target

    = "java".toCharArray() source.copyInto(target, endIndex = target.size) println(target) // kotl インデックスを適切に設定しないと ArrayIndexOutOfBoundsException
  77. associateWith キーのリストから値を関連付けてマップを作成可能 val keys = 'a'..'c' val map = keys.associateWith

    { it.toString().repeat(5).capitalize() } map.forEach { println(it) } // a=Aaaaa // b=Bbbbb // c=Ccccc
  78. associateWith キーのリストから値を関連付けてマップを作成可能 val keys = 'a'..'c' val map = keys.associateWith

    { it.toString().repeat(5).capitalize() } map.forEach { println(it) } // a=Aaaaa // b=Bbbbb // c=Ccccc キーに対する値を設定
  79. associateWith キーのリストから値を関連付けてマップを作成可能 val keys = 'a'..'c' val map = keys.associateWith

    { it.toString().repeat(5).capitalize() } map.forEach { println(it) } // a=Aaaaa // b=Bbbbb // c=Ccccc
  80. ifEmpty / ifBlank Collections, Map, Array 等が空の場合の値を指定可能 fun printAllUppercase(data: List<String>)

    { val result = data .filter { it.all { c -> c.isUpperCase() } } .ifEmpty { listOf("<no uppercase>") } result.forEach { println(it) } } printAllUppercase(listOf("foo", "Bar")) // <no uppercase> printAllUppercase(listOf("FOO", "bar")) // FOO
  81. ifEmpty / ifBlank Collections, Map, Array 等が空の場合の値を指定可能 fun printAllUppercase(data: List<String>)

    { val result = data .filter { it.all { c -> c.isUpperCase() } } .ifEmpty { listOf("<no uppercase>") } result.forEach { println(it) } } printAllUppercase(listOf("foo", "Bar")) // <no uppercase> printAllUppercase(listOf("FOO", "bar")) // FOO デフォルト値
  82. ifEmpty / ifBlank Collections, Map, Array 等が空の場合の値を指定可能 fun printAllUppercase(data: List<String>)

    { val result = data .filter { it.all { c -> c.isUpperCase() } } .ifEmpty { listOf("<no uppercase>") } result.forEach { println(it) } } printAllUppercase(listOf("foo", "Bar")) // <no uppercase> printAllUppercase(listOf("FOO", "bar")) // FOO
  83. その他 @SinceKotlin(“1.3”) で検索することで見つけられる @SinceKotlin("1.3") @kotlin.internal.InlineOnly public inline fun IntRange.random(): Int

    { return random(Random) }
  84. まとめ

  85. まとめ • Kotlin 1.3 の新機能についてかいつまんで説明した ◦ Coroutine ◦ Inline Classes

    ◦ Standard Libraries • Kotlin/Native, Contracts は後続の発表で • Kotlin 1.3 を使ってみよう!
  86. 参考 • What's New in Kotlin 1.3 ◦ https://kotl.in/1.3 •

    Kotlin Reference ◦ https://kotlinlang.org/docs/reference/coroutines-overview.html ◦ https://kotlinlang.org/docs/reference/inline-classes.html • GitHub ◦ https://github.com/JetBrains/kotlin
  87. Have a nice Kotlin!!