Upgrade to Pro — share decks privately, control downloads, hide ads and more …

コードで見るKotlin 1.3

コードで見るKotlin 1.3

Kohei Yamato

November 07, 2018
Tweet

More Decks by Kohei Yamato

Other Decks in Programming

Transcript

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

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

    • この中から3つをピックアップして紹介 ◦ Coroutines (ごく一部) ◦ Inline Classes ◦ Standard Libraries
  3. launch coroutine builder fun main() { GlobalScope.launch { delay(1000L) println("World!")

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

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

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

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

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

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

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

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

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

    println("World!") } println("Hello,") job.join() } => Hello, World!
  13. runBlocking coroutine builder fun main() { val job = GlobalScope.launch

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

    } println("Hello,") } 完了するまでメインスレッドをブ ロック
  15. CoroutineScope fun main() { val job = GlobalScope.launch { delay(1000L)

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

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

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

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

    } println("Hello,") } => Hello, World! coroutine が完了するまで アプリケーションは終了しない
  20. async coroutine builder fun main() = runBlocking { val x

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

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

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

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

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

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

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

    = async { delay(1000L); 10 } val y = async { delay(2000L); 20 } println(x.await() + y.await()) } 最大2000ms停止
  28. 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秒後)
  29. 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
  30. Coroutines まとめ • Coroutine builder を使用して作成する ◦ launch ◦ runBlocking

    ◦ async • 階層構造を持たせて実行するとよい
  31. プライマリコンストラクタのみ inline class Password(val value: String) fun main() { val

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

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

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

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

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

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

    String constructor_impl/* $FF was: constructor-impl*/(@NotNull String value) { Intrinsics.checkParameterIsNotNull(value, "value"); return value; } } 引数をそのまま返す
  39. プロパティ&メソッド 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) }
  40. プロパティ&メソッド 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つのみ
  41. プロパティ&メソッド 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) }
  42. プロパティ&メソッド 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) } それぞれ単純な処理
  43. プロパティ&メソッド (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); }
  44. プロパティ&メソッド (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 を返す
  45. プロパティ&メソッド (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 が呼ばれる
  46. プロパティ&メソッド (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); } }
  47. プロパティ&メソッド (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() を呼び出すだけ
  48. プロパティ&メソッド (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 を結合して出力
  49. Inline Classes まとめ • コンパイル時に static method として展開される • いくつかの制約がある

    ◦ プライマリコンストラクタのプロパティは1つ ◦ init 文を持てない, class を持てない, etc. • Experimental: 変更される可能性あり
  50. isNullOrEmpty / orEmpty Collection, Map, Array に追加で導入 fun main() {

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

    String? = null if (!s.isNullOrEmpty()) { println(s.length) } } スマートキャスト
  52. Array.copyInto 別の Array に要素をコピー可能に val source = "kotlin".toCharArray() val target

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

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

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

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

    { it.toString().repeat(5).capitalize() } map.forEach { println(it) } // a=Aaaaa // b=Bbbbb // c=Ccccc
  57. 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
  58. 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 デフォルト値
  59. 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
  60. まとめ • Kotlin 1.3 の新機能についてかいつまんで説明した ◦ Coroutine ◦ Inline Classes

    ◦ Standard Libraries • Kotlin/Native, Contracts は後続の発表で • Kotlin 1.3 を使ってみよう!
  61. 参考 • 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