Slide 1

Slide 1 text

イマドキの Kotlin 2018

Slide 2

Slide 2 text

Kotlinとは ● Java仮想マシンをターゲットとしたプログラミング言語 ○ AndroidやJavaScript, LLVMもサポート ● IntelliJ IDEAでおなじみのJetBrainsが開発 ● 2011年に発表され、2016年2月にver1.0がリリース ● 静的型付けオブジェクト指向言語 ● 簡潔、安全、Javaとの相互運用性 ● 拡張関数やデータクラス、Null安全などの便利機能

Slide 3

Slide 3 text

そんなこと知ってるんだな もう使い始めてるし

Slide 4

Slide 4 text

イマドキの Kotlin 2018

Slide 5

Slide 5 text

もくじ 1. Kotlin 1.1 2. KotlinConf 2017 3. Kotlin 1.2 4. KotlinでAndroid 5. KotlinでWeb

Slide 6

Slide 6 text

結論、というかメッセージ ● Kotlinは言語自体も成長途上にある! → 新バージョンをキャッチアップしてきましょう ● 活躍の場はAndroidだけじゃない! → サーバサイドも十分現実的な選択肢 → JavaScriptやNativeも

Slide 7

Slide 7 text

長澤 太郎 ● エムスリー株式会社 ● 日本Kotlinユーザグループ代表

Slide 8

Slide 8 text

1. Kotlin 1.1

Slide 9

Slide 9 text

Kotlin 1.1で導入・改善された文法や機能 ● コルーチン ● 型エイリアス ● メンバ参照(bound callable reference) ● ローカル変数で委譲プロパティ ● ラムダ式の引数を分解 ● 数値のアンスコ区切り ● 不要な変数を捨てる ● データクラスで継承可能に ● シールドクラスを同一ファイル内で継承可能に ● alsoやtakeIfなどが標準ライブラリに追加

Slide 10

Slide 10 text

コルーチン ● 実験的にKotlinに導入されているAPI・機能 ○ experimentalとはいえ、本番導入怖くない ● 関数を途中で抜けたり、続きから始めたりできる ● async/await→非同期処理を同期的に記述できる ○ コールバック地獄からの解放 suspend fun getMyUserId(): Long = ... suspend fun findFriends(id: Long): List = ... button.onClickListener { launch(UI) { val id = getMyUserId() val friends = findFriends(id) friendsView.addFriends(friends) } }

Slide 11

Slide 11 text

コルーチン ● 実験的にKotlinに導入されているAPI・機能 ○ experimentalとはいえ、本番導入怖くない ● 関数を途中で抜けたり、続きから始めたりできる ● async/await→非同期処理を同期的に記述できる ○ コールバック地獄からの解放 suspend fun getMyUserId(): Long = ... suspend fun findFriends(id: Long): List = ... button.onClickListener { launch(UI) { val id = getMyUserId() val friends = findFriends(id) friendsView.addFriends(friends) } }

Slide 12

Slide 12 text

型エイリアス ● 型に別名をつけることができる ● 型引数を多様する型だったり、関数型をわかりやすくするため に使うといいかも ● ValueObject的なことはデータクラスで typealias EventListener = (Event) -> Unit class MyComponent { var eventListener: EventListener = { e -> } } class User(val name: Name) // typealias Name = String data class Name(val value: String)

Slide 13

Slide 13 text

型エイリアス ● 型に別名をつけることができる ● 型引数を多様する型だったり、関数型をわかりやすくするため に使うといいかも ● ValueObject的なことはデータクラスで typealias EventListener = (Event) -> Unit class MyComponent { var eventListener: EventListener = { e -> } } class User(val name: Name) // typealias Name = String data class Name(val value: String) 型の組み合わせに名前を付けるイメージ

Slide 14

Slide 14 text

型エイリアス ● 型に別名をつけることができる ● 型引数を多様する型だったり、関数型をわかりやすくするため に使うといいかも ● ValueObject的なことはデータクラスで typealias EventListener = (Event) -> Unit class MyComponent { var eventListener: EventListener = { e -> } } class User(val name: Name) // typealias Name = String data class Name(val value: String) EventListenerに(Event) -> Unit オブジェクトを代入可能

Slide 15

Slide 15 text

型エイリアス ● 型に別名をつけることができる ● 型引数を多様する型だったり、関数型をわかりやすくするため に使うといいかも ● ValueObject的なことはデータクラスで typealias EventListener = (Event) -> Unit class MyComponent { var eventListener: EventListener = { e -> } } class User(val name: Name) // typealias Name = String data class Name(val value: String) 簡単に代入ミスは起こる val city = "Tokyo" User(city)

Slide 16

Slide 16 text

型エイリアス ● 型に別名をつけることができる ● 型引数を多様する型だったり、関数型をわかりやすくするため に使うといいかも ● ValueObject的なことはデータクラスで typealias EventListener = (Event) -> Unit class MyComponent { var eventListener: EventListener = { e -> } } class User(val name: Name) // typealias Name = String data class Name(val value: String) 型のご加護が val city = "Tokyo" User(city) //型不一致

Slide 17

Slide 17 text

メンバ参照 (bound callable reference) ● メンバ(メソッドやプロパティ)を、任意のオブジェクトと結びつけ て参照することができる、関数オブジェクト化することができる class UserService { private val log = LoggerFactory.getLogger("TEST") private fun log(users: List) { users.map(User::toJson).forEach(log::info) } ... }

Slide 18

Slide 18 text

メンバ参照 (bound callable reference) ● メンバ(メソッドやプロパティ)を、任意のオブジェクトと結びつけ て参照することができる、関数オブジェクト化することができる class UserService { private val log = LoggerFactory.getLogger("TEST") private fun log(users: List) { users.map(User::toJson).forEach(log::info) } ... } オブジェクトを結びつけていない参照 以前よりサポート

Slide 19

Slide 19 text

メンバ参照 (bound callable reference) ● メンバ(メソッドやプロパティ)を、任意のオブジェクトと結びつけ て参照することができる、関数オブジェクト化することができる class UserService { private val log = LoggerFactory.getLogger("TEST") private fun log(users: List) { users.map(User::toJson).forEach(log::info) } ... } オブジェクト(log)を結びつけた参照 ver 1.1よりサポート

Slide 20

Slide 20 text

ラムダ式の引数を分解&変数を捨てる ● componentN系メソッドを利用した分解宣言 ● これが可能なオブジェクトをラムダ式が引数に取るとき、引数 リスト内で分解が可能 listOf( Triple(1, "Java", "Oracle"), Triple(2, "Kotlin", "JetBrains) ) .forEach { (_, name, owner) -> showMessage(name, owner) }

Slide 21

Slide 21 text

ラムダ式の引数を分解&変数を捨てる ● componentN系メソッドを利用した分解宣言 ● これが可能なオブジェクトをラムダ式が引数に取るとき、引数 リスト内で分解が可能 listOf( Triple(1, "Java", "Oracle"), Triple(2, "Kotlin", "JetBrains) ) .forEach { (_, name, owner) -> showMessage(name, owner) }

Slide 22

Slide 22 text

ラムダ式の引数を分解&変数を捨てる ● componentN系メソッドを利用した分解宣言 ● これが可能なオブジェクトをラムダ式が引数に取るとき、引数 リスト内で分解が可能 listOf( Triple(1, "Java", "Oracle"), Triple(2, "Kotlin", "JetBrains) ) .forEach { (_, name, owner) -> showMessage(name, owner) } 不要な変数(引数)はアンダースコアで受け、捨てる

Slide 23

Slide 23 text

データクラス、シールドクラス制限緩和 ● データクラスが他のクラスを継承することが可能に ● シールドクラスを同一ファイル内に定義されたクラスから継承 することが可能に sealed class MyList object Nil: MyList() data class Cons(val head: T, val tail: MyList): MyList()

Slide 24

Slide 24 text

2. KotlinConf 2017

Slide 25

Slide 25 text

KotlinConf 行ってきました!

Slide 26

Slide 26 text

KotlinConf 行ってきました! ● JetBrains主催のKotlinカンファレンス ● 11/2と11/3の2日間 ● 会場はサンフランシスコ ● 全44セッション、3トラック ● 1,200枚のチケットは完売 ● 日本からは @shiraj_iさんが登壇!

Slide 27

Slide 27 text

keynoteでマルチプラットフォームが話題に

Slide 28

Slide 28 text

keynoteでマルチプラットフォームが話題に ● マルチプラットフォームでのコードの共有を可 能にする ● 例えば ○ ビジネスロジックやデータ構造などを共有 ■ 実装だけでなく宣言も共有することが可能 ○ ビューやデータアクセス、低レベル処理は各プラット フォーム固有 ● 現在はexperimental ● JVMとJSをサポート、Nativeはそのうち

Slide 29

Slide 29 text

サーバサイドKotlin キテます

Slide 30

Slide 30 text

3. Kotlin 1.2

Slide 31

Slide 31 text

Kotlin 1.2で導入・改善された文法や機能 ● マルチプラットフォーム・プロジェクト ● アノテーション引数における配列リテラル ● lateinitプロパティのメタ属性 isInitialized導入 ● bound callable referenceの改善 ● 型推論の改善 ● スマートキャストの改善 ● その他ツールやライブラリの改善など

Slide 32

Slide 32 text

マルチプラットフォーム・プロジェクト ● マルチプラットフォームでのコードの共有を可能にする ● 例えば ○ ビジネスロジックやデータ構造などを共有 ■ 実装だけでなく宣言も共有することが可能 ○ ビューやデータアクセス、低レベル処理は各プラットフォーム固有 ● 現在はexperimental ● JVMとJSをサポート、Nativeはそのうち

Slide 33

Slide 33 text

マルチプラットフォーム・プロジェクトの構造 Common Platform (JVM) Platform (JS) Regular (JVM) Regular (JS) 矢印は 依存の方向

Slide 34

Slide 34 text

実装だけでなく宣言も共有可能 // Common module expect fun hello() // JVM module actual fun hello() { println("Hello, JVM!") } // JS module actual fun hello() { println("Hello, JS!") }

Slide 35

Slide 35 text

実装だけでなく宣言も共有可能 // Common module expect fun hello() // JVM module actual fun hello() { println("Hello, JVM!") } // JS module actual fun hello() { println("Hello, JS!") }

Slide 36

Slide 36 text

マルプラ・プロジェクト始め方

Slide 37

Slide 37 text

アノテーション引数における配列リテラル annotation class Foo(val value: Array) @Foo(arrayOf("hoge", "fuga")) class Bar @Foo(["hoge", "fuga"]) class Bar

Slide 38

Slide 38 text

可変長引数と配列リテラル @Bar(["hoge"]) annotation class Bar(vararg val value: String) @Bar(value = ["hoge"]) @Bar("hoge") @Bar(value = "hoge") @Bar(*["hoge"]) OK! Compilation error OK! OK! Deprecated

Slide 39

Slide 39 text

bound callabel referenceの改善 object Foo { fun square(n: Int) = n * n fun squareOf4() = (this::square)(4) fun squareOf5() = 5.let(this::square) } object Foo { fun square(n: Int) = n * n fun squareOf4() = (::square)(4) fun squareOf5() = 5.let(::square) }

Slide 40

Slide 40 text

4. KotlinでAndroid

Slide 41

Slide 41 text

Parcelable ● Parcelにデータを書くために実装すべき形式・ルール ● 保存: Parcelableインタフェースを実装する ● 復元: Parcelable.Creatorインタフェースを実装したオブ ジェクトをstaticフィールドCREATORとして公開する

Slide 42

Slide 42 text

Parcelable ● Parcelにデータを書くために実装すべき形式・ルール ● 保存: Parcelableインタフェースを実装する ● 復元: Parcelable.Creatorインタフェースを実装したオブ ジェクトをstaticフィールドCREATORとして公開する Kotlinには staticもフィールドもない!

Slide 43

Slide 43 text

コンパニオンオブジェクト class Foo { companion object { val name: String = "Foo" } } Foo.name //=> Foo

Slide 44

Slide 44 text

class Foo { companion object { val name: String = "Foo" } } Foo.name //=> Foo コンパニオンオブジェクト コンパニオンオブジェクト

Slide 45

Slide 45 text

class Foo { companion object { val name: String = "Foo" } } Foo.name //=> Foo コンパニオンオブジェクト staticっぽい! フィールドっぽい! →でも違う

Slide 46

Slide 46 text

JVMと仲良くするアノテーション class Foo { companion object { @JvmField val name: String = "Foo" } }

Slide 47

Slide 47 text

class Foo { companion object { @JvmField val name: String = "Foo" } } JVMと仲良くするアノテーション staticフィールドとして見えるようになる

Slide 48

Slide 48 text

1.1.4から加わった@Parcelizeを使う ● 対象のクラスをいい感じにParcelableにしてくれる ● Parcelableインタフェースを実装して ● @Parcelizeアノテーションをクラスに付けるだけ

Slide 49

Slide 49 text

KODEIN ● Kotlin製DependencyInjectionライブラリ ● kapt不使用

Slide 50

Slide 50 text

依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter: Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } class App: Application() { val kodein: Kodein by Kodein.lazy { bind("en") with provider { EnglishGreeter() } bind("ja") with singleton { JapaneseGreeter() } bind() with singleton { Human(instance("ja")) } } }

Slide 51

Slide 51 text

依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter: Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } class App: Application() { val kodein: Kodein by Kodein.lazy { bind("en") with provider { EnglishGreeter() } bind("ja") with singleton { JapaneseGreeter() } bind() with singleton { Human(instance("ja")) } } } インタフェースが1つとその実装が2つある

Slide 52

Slide 52 text

依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter: Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } class App: Application() { val kodein: Kodein by Kodein.lazy { bind("en") with provider { EnglishGreeter() } bind("ja") with singleton { JapaneseGreeter() } bind() with singleton { Human(instance("ja")) } } } Human has a Greeter

Slide 53

Slide 53 text

依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter: Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } class App: Application() { val kodein: Kodein by Kodein.lazy { bind("en") with provider { EnglishGreeter() } bind("ja") with singleton { JapaneseGreeter() } bind() with singleton { Human(instance("ja")) } } }

Slide 54

Slide 54 text

依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter: Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } class App: Application() { val kodein: Kodein by Kodein.lazy { bind("en") with provider { EnglishGreeter() } bind("ja") with singleton { JapaneseGreeter() } bind() with singleton { Human(instance("ja")) } } } 型と実装をバインドする 同一の型であっても名前で区別可能

Slide 55

Slide 55 text

依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter: Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } class App: Application() { val kodein: Kodein by Kodein.lazy { bind("en") with provider { EnglishGreeter() } bind("ja") with singleton { JapaneseGreeter() } bind() with singleton { Human(instance("ja")) } } } 実装を注入する度に呼び出される

Slide 56

Slide 56 text

依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter: Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } class App: Application() { val kodein: Kodein by Kodein.lazy { bind("en") with provider { EnglishGreeter() } bind("ja") with singleton { JapaneseGreeter() } bind() with singleton { Human(instance("ja")) } } } シングルトン

Slide 57

Slide 57 text

依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter: Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } class App: Application() { val kodein: Kodein by Kodein.lazy { bind("en") with provider { EnglishGreeter() } bind("ja") with singleton { JapaneseGreeter() } bind() with singleton { Human(instance("ja")) } } } 実装をそれに依存しない形で注入

Slide 58

Slide 58 text

依存の注入 class MainActivity: AppCompatActivity() { private val injector = KodeinInjector() private val speaker: Speaker by injector.instance() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) injector.inject((application as App).kodein) ... button.setOnClickListener { speaker.speak() } } }

Slide 59

Slide 59 text

依存の注入 class MainActivity: AppCompatActivity() { private val injector = KodeinInjector() private val speaker: Speaker by injector.instance() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) injector.inject((application as App).kodein) ... button.setOnClickListener { speaker.speak() } } } おまじない

Slide 60

Slide 60 text

依存の注入 class MainActivity: AppCompatActivity() { private val injector = KodeinInjector() private val speaker: Speaker by injector.instance() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) injector.inject((application as App).kodein) ... button.setOnClickListener { speaker.speak() } } } 委譲プロパティによる注入

Slide 61

Slide 61 text

依存の注入 class MainActivity: AppCompatActivity() { private val injector = KodeinInjector() private val speaker: Speaker by injector.instance() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) injector.inject((application as App).kodein) ... button.setOnClickListener { speaker.speak() } } } 注入された実装を使うことができる

Slide 62

Slide 62 text

依存の注入 class MainActivity: AppCompatActivity(), KodeinInjected { override val injector = KodeinInjector() private val speaker: Speaker by instance() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) inject((application as App).kodein) ... button.setOnClickListener { speaker.speak() } } } これを実装すると ちょっとだけ記述量が減る!!

Slide 63

Slide 63 text

KOIN ● Kotlin製DependencyInjectionライブラリ ● kapt不使用

Slide 64

Slide 64 text

依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter: Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } import org.koin.dsl.module.applicationContext as appContext class App: Application() { private val module = appContext { factory("en") { EnglishGreeter() } provide("ja") { JapaneseGreeter() } provide { Human(get("ja")) } } override fun onCreate() { super.onCreate() startKoin(this, listOf(module)) } }

Slide 65

Slide 65 text

依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter: Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } import org.koin.dsl.module.applicationContext as appContext class App: Application() { private val module = appContext { factory("en") { EnglishGreeter() } provide("ja") { JapaneseGreeter() } provide { Human(get("ja")) } } override fun onCreate() { super.onCreate() startKoin(this, listOf(module)) } }

Slide 66

Slide 66 text

依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter: Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } import org.koin.dsl.module.applicationContext as appContext class App: Application() { private val module = appContext { factory("en") { EnglishGreeter() } provide("ja") { JapaneseGreeter() } provide { Human(get("ja")) } } override fun onCreate() { super.onCreate() startKoin(this, listOf(module)) } } 毎回生成されるやつ 名前を付けることも可能

Slide 67

Slide 67 text

依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter: Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } import org.koin.dsl.module.applicationContext as appContext class App: Application() { private val module = appContext { factory("en") { EnglishGreeter() } provide("ja") { JapaneseGreeter() } provide { Human(get("ja")) } } override fun onCreate() { super.onCreate() startKoin(this, listOf(module)) } } シングルトン もちろん名前を付けられる

Slide 68

Slide 68 text

依存の定義 interface Greeter { fun hello(): String } class EnglishGreeter: Greeter { ... } class JapaneseGreeter: Greeter { ... } interface Speaker { fun speak() } class Human(val greeter: Greeter): Speaker { ... } import org.koin.dsl.module.applicationContext as appContext class App: Application() { private val module = appContext { factory("en") { EnglishGreeter() } provide("ja") { JapaneseGreeter() } provide { Human(get("ja")) } } override fun onCreate() { super.onCreate() startKoin(this, listOf(module)) } } 実装をそれに依存しない形で注入

Slide 69

Slide 69 text

依存の注入 class MainActivity: AppCompatActivity() { private val speaker: Speaker by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... button.setOnClickListener { speaker.speak() } } }

Slide 70

Slide 70 text

依存の注入 class MainActivity: AppCompatActivity() { private val speaker: Speaker by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... button.setOnClickListener { speaker.speak() } } } 委譲プロパティで注入

Slide 71

Slide 71 text

5. KotlinでWeb

Slide 72

Slide 72 text

Kotlin x Spring ● Spring Boot 2 (Spring Framework 5)から、Kotlinフレンド リ感がさらにアップ! ○ Spring WebFluxの追加 ■ ノンブロッキングでリアクティブなWebフレームワーク ■ 従来のアノテーションによるハンドラの宣言に加え ■ ラムダを使用したルーティング ○ ラムダを使用したBean登録 ○ KotlinフレンドリなAPI群の追加 ● コルーチンの活用

Slide 73

Slide 73 text

従来のスタイル @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): String { val itemA = demoService.getItemA() val itemB = demoService.getItemB() val itemC = demoService.getItemC(itemA, itemB) return itemC.answer } }

Slide 74

Slide 74 text

従来のスタイル @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): String { val itemA = demoService.getItemA() val itemB = demoService.getItemB() val itemC = demoService.getItemC(itemA, itemB) return itemC.answer } }

Slide 75

Slide 75 text

従来のスタイル @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): String { val itemA = demoService.getItemA() val itemB = demoService.getItemB() val itemC = demoService.getItemC(itemA, itemB) return itemC.answer } }

Slide 76

Slide 76 text

従来のスタイル @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): String { val itemA = demoService.getItemA() val itemB = demoService.getItemB() val itemC = demoService.getItemC(itemA, itemB) return itemC.answer } }

Slide 77

Slide 77 text

従来のスタイル @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): String { val itemA = demoService.getItemA() val itemB = demoService.getItemB() val itemC = demoService.getItemC(itemA, itemB) return itemC.answer } }

Slide 78

Slide 78 text

従来のスタイル @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): String { val itemA = demoService.getItemA() val itemB = demoService.getItemB() val itemC = demoService.getItemC(itemA, itemB) return itemC.answer } }

Slide 79

Slide 79 text

WebFlux @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): Mono = Mono.zip( demoService.getMonoA(), demoService.getMonoB() ) .flatMap { demoService.getMonoC(it.t1, it.t2) } .map { it.answer } } // dependencies compile('org.springframework.boot:spring-boot-starter-webflux')

Slide 80

Slide 80 text

WebFlux @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): Mono = Mono.zip( demoService.getMonoA(), demoService.getMonoB() ) .flatMap { demoService.getMonoC(it.t1, it.t2) } .map { it.answer } } // dependencies compile('org.springframework.boot:spring-boot-starter-webflux')

Slide 81

Slide 81 text

@RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): Mono = Mono.zip( demoService.getMonoA(), demoService.getMonoB() ) .flatMap { demoService.getMonoC(it.t1, it.t2) } .map { it.answer } } WebFlux // dependencies compile('org.springframework.boot:spring-boot-starter-webflux')

Slide 82

Slide 82 text

@RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): Mono = Mono.zip( demoService.getMonoA(), demoService.getMonoB() ) .flatMap { demoService.getMonoC(it.t1, it.t2) } .map { it.answer } } WebFlux // dependencies compile('org.springframework.boot:spring-boot-starter-webflux')

Slide 83

Slide 83 text

@RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): Mono = Mono.zip( demoService.getMonoA(), demoService.getMonoB() ) .flatMap { demoService.getMonoC(it.t1, it.t2) } .map { it.answer } } WebFlux // dependencies compile('org.springframework.boot:spring-boot-starter-webflux')

Slide 84

Slide 84 text

@RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): Mono = Mono.zip( demoService.getMonoA(), demoService.getMonoB() ) .flatMap { demoService.getMonoC(it.t1, it.t2) } .map { it.answer } } WebFlux // dependencies compile('org.springframework.boot:spring-boot-starter-webflux')

Slide 85

Slide 85 text

Reactorでコルーチン @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): Mono = mono { val a = async { demoService.getItemA() } val b = async { demoService.getItemB() } val c = async { demoService.getItemC(a.await(), b.await()) } c.await().answer } } // dependencies compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.19.3' compile 'org.jetbrains.kotlinx:kotlinx-coroutines-reactor:0.19.3'

Slide 86

Slide 86 text

Reactorでコルーチン @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): Mono = mono { val a = async { demoService.getItemA() } val b = async { demoService.getItemB() } val c = async { demoService.getItemC(a.await(), b.await()) } c.await().answer } } // dependencies compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.19.3' compile 'org.jetbrains.kotlinx:kotlinx-coroutines-reactor:0.19.3'

Slide 87

Slide 87 text

Reactorでコルーチン @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): Mono = mono { val a = async { demoService.getItemA() } val b = async { demoService.getItemB() } val c = async { demoService.getItemC(a.await(), b.await()) } c.await().answer } } // dependencies compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.19.3' compile 'org.jetbrains.kotlinx:kotlinx-coroutines-reactor:0.19.3'

Slide 88

Slide 88 text

Reactorでコルーチン @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): Mono = mono { val a = async { demoService.getItemA() } val b = async { demoService.getItemB() } val c = async { demoService.getItemC(a.await(), b.await()) } c.await().answer } } // dependencies compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.19.3' compile 'org.jetbrains.kotlinx:kotlinx-coroutines-reactor:0.19.3'

Slide 89

Slide 89 text

Reactorでコルーチン @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): Mono = mono { val a = async { demoService.getItemA() } val b = async { demoService.getItemB() } val c = async { demoService.getItemC(a.await(), b.await()) } c.await().answer } } // dependencies compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.19.3' compile 'org.jetbrains.kotlinx:kotlinx-coroutines-reactor:0.19.3'

Slide 90

Slide 90 text

ハンドラメソッドをサスペンドに @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") suspend fun handle():String { val a = async { demoService.getItemA() } val b = async { demoService.getItemB() } val c = async { demoService.getItemC(a.await(), b.await()) } return c.await().answer } } // dependencies compile 'org.springframework.kotlin:spring-webflux-kotlin-coroutine:0.3.1'

Slide 91

Slide 91 text

ハンドラメソッドをサスペンドに @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") suspend fun handle():String { val a = async { demoService.getItemA() } val b = async { demoService.getItemB() } val c = async { demoService.getItemC(a.await(), b.await()) } return c.await().answer } } // dependencies compile 'org.springframework.kotlin:spring-webflux-kotlin-coroutine:0.3.1'

Slide 92

Slide 92 text

Exposed ● Kotlin SQLライブラリ ○ SQL DSL ○ Data Access Object ● JetBrainsが開発 https://github.com/JetBrains/Exposed ● 有名なDBMSに対応: ポスグレ, MySQL, Oracle, ...

Slide 93

Slide 93 text

SQL DSL object Teams: Table() { val id = long("id").autoIncrement().primaryKey() val name = varchar("name", length = 100) } fun main(args: Array) { Database.connect("jdbc:h2:mem:test", "org.h2.Driver") transaction { SchemaUtils.create(Teams) Teams.insert { it[name] = "Kotlin" } Teams.selectAll().forEach { row -> println("id=${row[Teams.id]}: ${row[Teams.name]}") // id=1: Kotlin } SchemaUtils.drop(Teams) } }

Slide 94

Slide 94 text

SQL DSL object Teams: Table() { val id = long("id").autoIncrement().primaryKey() val name = varchar("name", length = 100) } fun main(args: Array) { Database.connect("jdbc:h2:mem:test", "org.h2.Driver") transaction { SchemaUtils.create(Teams) Teams.insert { it[name] = "Kotlin" } Teams.selectAll().forEach { row -> println("id=${row[Teams.id]}: ${row[Teams.name]}") // id=1: Kotlin } SchemaUtils.drop(Teams) } } テーブル定義

Slide 95

Slide 95 text

SQL DSL object Teams: Table() { val id = long("id").autoIncrement().primaryKey() val name = varchar("name", length = 100) } fun main(args: Array) { Database.connect("jdbc:h2:mem:test", "org.h2.Driver") transaction { SchemaUtils.create(Teams) Teams.insert { it[name] = "Kotlin" } Teams.selectAll().forEach { row -> println("id=${row[Teams.id]}: ${row[Teams.name]}") // id=1: Kotlin } SchemaUtils.drop(Teams) } } DBに接続

Slide 96

Slide 96 text

SQL DSL object Teams: Table() { val id = long("id").autoIncrement().primaryKey() val name = varchar("name", length = 100) } fun main(args: Array) { Database.connect("jdbc:h2:mem:test", "org.h2.Driver") transaction { SchemaUtils.create(Teams) Teams.insert { it[name] = "Kotlin" } Teams.selectAll().forEach { row -> println("id=${row[Teams.id]}: ${row[Teams.name]}") // id=1: Kotlin } SchemaUtils.drop(Teams) } } トランザクション

Slide 97

Slide 97 text

SQL DSL object Teams: Table() { val id = long("id").autoIncrement().primaryKey() val name = varchar("name", length = 100) } fun main(args: Array) { Database.connect("jdbc:h2:mem:test", "org.h2.Driver") transaction { SchemaUtils.create(Teams) Teams.insert { it[name] = "Kotlin" } Teams.selectAll().forEach { row -> println("id=${row[Teams.id]}: ${row[Teams.name]}") // id=1: Kotlin } SchemaUtils.drop(Teams) } } テーブル作成 テーブル削除

Slide 98

Slide 98 text

SQL DSL object Teams: Table() { val id = long("id").autoIncrement().primaryKey() val name = varchar("name", length = 100) } fun main(args: Array) { Database.connect("jdbc:h2:mem:test", "org.h2.Driver") transaction { SchemaUtils.create(Teams) Teams.insert { it[name] = "Kotlin" } Teams.selectAll().forEach { row -> println("id=${row[Teams.id]}: ${row[Teams.name]}") // id=1: Kotlin } SchemaUtils.drop(Teams) } } インサート

Slide 99

Slide 99 text

SQL DSL object Teams: Table() { val id = long("id").autoIncrement().primaryKey() val name = varchar("name", length = 100) } fun main(args: Array) { Database.connect("jdbc:h2:mem:test", "org.h2.Driver") transaction { SchemaUtils.create(Teams) Teams.insert { it[name] = "Kotlin" } Teams.selectAll().forEach { row -> println("id=${row[Teams.id]}: ${row[Teams.name]}") // id=1: Kotlin } SchemaUtils.drop(Teams) } } 取得

Slide 100

Slide 100 text

一対多 関連 object Teams: Table() { val id = long("id").autoIncrement().primaryKey() val name = varchar("name", length = 100) } object Tasks: Table() { val id = long("id").autoIncrement().primaryKey() val content = text("content") val completedAt = datetime("completed_at").nullable() val teamId = long("team_id") references Teams.id }

Slide 101

Slide 101 text

一対多 関連 object Teams: Table() { val id = long("id").autoIncrement().primaryKey() val name = varchar("name", length = 100) } object Tasks: Table() { val id = long("id").autoIncrement().primaryKey() val content = text("content") val completedAt = datetime("completed_at").nullable() val teamId = long("team_id") references Teams.id } 外部キー設定

Slide 102

Slide 102 text

joinとprojectionとwhere Tasks.innerJoin(Teams) .slice(Team.name, Tasks.content) .select { Tasks.completedAt.isNull() } .forEach { row -> println("${row[Teams.name]}: ${row[Tasks.content]}") } id name 1 Kotlin id content completed_at team_id 1 release 1.1.60 2017-11-14T03:00+09:00 1 2 release 1.2.0 (null) 1 Teams Tasks Teams.name Tasks.content Kotlin release 1.2.0

Slide 103

Slide 103 text

Data Access Object object Teams: LongIdTable() { val name = varchar("name", length = 100) } class Team(id: EntityID): LongEntity(id) { companion object: LongEntityClass(Teams) var name by Teams.name } fun main(args: Array) { Database.connect("jdbc:h2:mem:test", "org.h2.Driver") transaction { SchemaUtils.create(Teams) Team.new { name = "Kotlin" } Team.all().forEach { team: Team -> println("id=${team.id}, name=${team.name}") } } }

Slide 104

Slide 104 text

Data Access Object object Teams: LongIdTable() { val name = varchar("name", length = 100) } class Team(id: EntityID): LongEntity(id) { companion object: LongEntityClass(Teams) var name by Teams.name } fun main(args: Array) { Database.connect("jdbc:h2:mem:test", "org.h2.Driver") transaction { SchemaUtils.create(Teams) Team.new { name = "Kotlin" } Team.all().forEach { team: Team -> println("id=${team.id}, name=${team.name}") } } }

Slide 105

Slide 105 text

Data Access Object object Teams: LongIdTable() { val name = varchar("name", length = 100) } class Team(id: EntityID): LongEntity(id) { companion object: LongEntityClass(Teams) var name by Teams.name } fun main(args: Array) { Database.connect("jdbc:h2:mem:test", "org.h2.Driver") transaction { SchemaUtils.create(Teams) Team.new { name = "Kotlin" } Team.all().forEach { team: Team -> println("id=${team.id}, name=${team.name}") } } }

Slide 106

Slide 106 text

一対多 関連 object Teams: LongIdTable() { val name = varchar("name", length = 100) } object Tasks: LongIdTable() { val content: text("content") val completedAt: datetime("completed_at").nullable() val team = reference("team", Teams) } class Team(id: EntityID): LongEntity(id) { companion object: LongEntityClass(Teams) var name by Teams.name val tasks by Tasks.referrersOn(Tasks.team) } class Task(id: EntityID): LongEntity(id) { companion object: LongEntityClass(Tasks) var content by Tasks.content var completedAt by Tasks.completedAt var team by Team.referencedOn(Tasks.team) }

Slide 107

Slide 107 text

一対多 関連 object Teams: LongIdTable() { val name = varchar("name", length = 100) } object Tasks: LongIdTable() { val content: text("content") val completedAt: datetime("completed_at").nullable() val team = reference("team", Teams) } class Team(id: EntityID): LongEntity(id) { companion object: LongEntityClass(Teams) var name by Teams.name val tasks by Tasks.referrersOn(Tasks.team) } class Task(id: EntityID): LongEntity(id) { companion object: LongEntityClass(Tasks) var content by Tasks.content var completedAt by Tasks.completedAt var team by Team.referencedOn(Tasks.team) } 関連を定義

Slide 108

Slide 108 text

取得 Task .find { Tasks.completedAt.isNull() } .forEach { task: Task -> val team: Team = task.team println("${team.name}: ${task.content}") }

Slide 109

Slide 109 text

取得 Task .find { Tasks.completedAt.isNull() } .forEach { task: Task -> val team: Team = task.team println("${team.name}: ${task.content}") } N + 1 のおそれ

Slide 110

Slide 110 text

無理やりまとめると ● コルーチンすごい ○ Androidはもちろん、サーバサイド(WebFluxなど)でも便利に使える ● サーバサイドでもKotlin ○ KotlinConfでのセッション、多い ○ Spring ♡ Kotlin ○ Exposed...JetBrainsによるKotlin製ORM ○ (Ktor...JetBrainsによるKotlin製Webマイクロフレームワーク) ● 3rd party製 Kotlinライブラリ、フレームワーク ○ KODEIN ○ KOIN

Slide 111

Slide 111 text

メッセージ(再掲) ● Kotlinは言語自体も成長途上にある! → 新バージョンをキャッチアップしてきましょう ● 活躍の場はAndroidだけじゃない! → サーバサイドも十分現実的な選択肢 → JavaScriptやNativeも