Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

自己紹介 ● 大和 康平 (24) ● エムスリー 2018年4月入社 ● 仕事の担当: サーバーサイド ● 趣味: モータースポーツ観戦

Slide 3

Slide 3 text

発表の概要 ● 基本的にはこちらの内容 ○ What's New in Kotlin 1.3 (https://kotl.in/1.3) ● この中から3つをピックアップして紹介 ○ Coroutines (ごく一部) ○ Inline Classes ○ Standard Libraries

Slide 4

Slide 4 text

Kotlin 1.3 リリース

Slide 5

Slide 5 text

Kotlin 1.3 事始め コマンドラインの場合: Gradleの場合: $ brew install kotlin plugins { id 'org.jetbrains.kotlin.jvm' version '1.3.0' }

Slide 6

Slide 6 text

Coroutines

Slide 7

Slide 7 text

Coroutines ● 基本は軽量なスレッド ● CoroutineScope 中の coroutine builder により起動する ○ launch ○ runBlocking ○ async

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

GrobalScope ● CoroutineScope の一種 ● ライフタイム: アプリケーション終了時まで

Slide 12

Slide 12 text

launch coroutine builder ● 現在のスレッドをブロックせずに、新しい coroutine を起動 する ● Coroutine への参照を Job として返す (後述)

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

Job fun main() { GlobalScope.launch { delay(1000L) println("World!") } println("Hello,") Thread.sleep(2000L) } coroutineの内容によらず 2000ms停止......

Slide 20

Slide 20 text

Job fun main() { val job = GlobalScope.launch { delay(1000L) println("World!") } println("Hello,") job.join() }

Slide 21

Slide 21 text

Job ● launch coroutine builder の返り値 ● いくつかの状態を持つ ○ New, Active, Completing, Cancelling, Cancelled, Completed ● Job#join を実行することにより、完了するまで呼び出し元 の coroutine を停止できる

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

Job fun main() { val job = GlobalScope.launch { delay(1000L) println("World!") } println("Hello,") job.join() } => Hello, World!

Slide 24

Slide 24 text

runBlocking coroutine builder ● 現在のスレッドをブロックして、新しい coroutine を起動する ● コードブロックの返り値をそのまま返す

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

runBlocking coroutine builder fun main() { runBlocking { delay(1000L) println("World!") } println("Hello,") } => World! (約1秒後) Hello,

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

CoroutineScope ● 全ての coroutine builder は、そのコードブロックのスコープ に CroutineScope のインスタンスを追加する ● 外のコルーチンは、スコープ中で起動されたコルーチンが全 て終了するまで完了しない

Slide 34

Slide 34 text

CoroutineScope fun main() = runBlocking { launch { delay(1000L) println("World!") } println("Hello,") } => Hello, World! coroutine が完了するまで アプリケーションは終了しない

Slide 35

Slide 35 text

async coroutine builder ● 現在のスレッドをブロックせずに、新しい coroutine を起動 する ○ launch と同じ ● 将来の実行結果を Deferred として返す

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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秒後)

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

Coroutines まとめ ● Coroutine builder を使用して作成する ○ launch ○ runBlocking ○ async ● 階層構造を持たせて実行するとよい

Slide 47

Slide 47 text

Inline Classes [experimental]

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

プライマリコンストラクタのみ (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

Slide 56

Slide 56 text

プライマリコンストラクタのみ (decompiled) public final class Password { @NotNull public static String constructor_impl/* $FF was: constructor-impl*/(@NotNull String value) { Intrinsics.checkParameterIsNotNull(value, "value"); return value; } } 引数をそのまま返す

Slide 57

Slide 57 text

プロパティ&メソッド 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) }

Slide 58

Slide 58 text

プロパティ&メソッド 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つのみ

Slide 59

Slide 59 text

プロパティ&メソッド 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) }

Slide 60

Slide 60 text

プロパティ&メソッド 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) } それぞれ単純な処理

Slide 61

Slide 61 text

プロパティ&メソッド (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); }

Slide 62

Slide 62 text

プロパティ&メソッド (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 を返す

Slide 63

Slide 63 text

プロパティ&メソッド (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 が呼ばれる

Slide 64

Slide 64 text

プロパティ&メソッド (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); } }

Slide 65

Slide 65 text

プロパティ&メソッド (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() を呼び出すだけ

Slide 66

Slide 66 text

プロパティ&メソッド (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 を結合して出力

Slide 67

Slide 67 text

Inline Classes まとめ ● コンパイル時に static method として展開される ● いくつかの制約がある ○ プライマリコンストラクタのプロパティは1つ ○ init 文を持てない, class を持てない, etc. ● Experimental: 変更される可能性あり

Slide 68

Slide 68 text

Standard Libraries

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

isNullOrEmpty / orEmpty Collection, Map, Array に追加で導入 fun main() { val s: String? = null if (!s.isNullOrEmpty()) { println(s.length) } }

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

isNullOrEmpty / orEmpty Contracts によりスマートキャストできる @kotlin.internal.InlineOnly public inline fun CharSequence?.isNullOrEmpty(): Boolean { contract { return(false) implies (this@isNullOrEmpty != null) } return random(Random) }

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

Array.copyInto 別の Array に要素をコピー可能に val source = "kotlin".toCharArray() val target = "java".toCharArray() source.copyInto(target, endIndex = target.size) println(target) // kotl インデックスを適切に設定しないと ArrayIndexOutOfBoundsException

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

その他 @SinceKotlin(“1.3”) で検索することで見つけられる @SinceKotlin("1.3") @kotlin.internal.InlineOnly public inline fun IntRange.random(): Int { return random(Random) }

Slide 84

Slide 84 text

まとめ

Slide 85

Slide 85 text

まとめ ● Kotlin 1.3 の新機能についてかいつまんで説明した ○ Coroutine ○ Inline Classes ○ Standard Libraries ● Kotlin/Native, Contracts は後続の発表で ● Kotlin 1.3 を使ってみよう!

Slide 86

Slide 86 text

参考 ● 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

Slide 87

Slide 87 text

Have a nice Kotlin!!