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

イマドキのKotlin 2018

イマドキのKotlin 2018

14c9795d267f5b85abb98ca5e8780646?s=128

Taro Nagasawa

January 25, 2018
Tweet

Transcript

  1. イマドキの Kotlin 2018

  2. Kotlinとは • Java仮想マシンをターゲットとしたプログラミング言語 ◦ AndroidやJavaScript, LLVMもサポート • IntelliJ IDEAでおなじみのJetBrainsが開発 •

    2011年に発表され、2016年2月にver1.0がリリース • 静的型付けオブジェクト指向言語 • 簡潔、安全、Javaとの相互運用性 • 拡張関数やデータクラス、Null安全などの便利機能
  3. そんなこと知ってるんだな もう使い始めてるし

  4. イマドキの Kotlin 2018

  5. もくじ 1. Kotlin 1.1 2. KotlinConf 2017 3. Kotlin 1.2

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

    JavaScriptやNativeも
  7. 長澤 太郎 • エムスリー株式会社 • 日本Kotlinユーザグループ代表

  8. 1. Kotlin 1.1

  9. Kotlin 1.1で導入・改善された文法や機能 • コルーチン • 型エイリアス • メンバ参照(bound callable reference)

    • ローカル変数で委譲プロパティ • ラムダ式の引数を分解 • 数値のアンスコ区切り • 不要な変数を捨てる • データクラスで継承可能に • シールドクラスを同一ファイル内で継承可能に • alsoやtakeIfなどが標準ライブラリに追加
  10. コルーチン • 実験的にKotlinに導入されているAPI・機能 ◦ experimentalとはいえ、本番導入怖くない • 関数を途中で抜けたり、続きから始めたりできる • async/await→非同期処理を同期的に記述できる ◦

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

    コールバック地獄からの解放 suspend fun getMyUserId(): Long = ... suspend fun findFriends(id: Long): List<User> = ... button.onClickListener { launch(UI) { val id = getMyUserId() val friends = findFriends(id) friendsView.addFriends(friends) } }
  12. 型エイリアス • 型に別名をつけることができる • 型引数を多様する型だったり、関数型をわかりやすくするため に使うといいかも • 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)
  13. 型エイリアス • 型に別名をつけることができる • 型引数を多様する型だったり、関数型をわかりやすくするため に使うといいかも • 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) 型の組み合わせに名前を付けるイメージ
  14. 型エイリアス • 型に別名をつけることができる • 型引数を多様する型だったり、関数型をわかりやすくするため に使うといいかも • 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 オブジェクトを代入可能
  15. 型エイリアス • 型に別名をつけることができる • 型引数を多様する型だったり、関数型をわかりやすくするため に使うといいかも • 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)
  16. 型エイリアス • 型に別名をつけることができる • 型引数を多様する型だったり、関数型をわかりやすくするため に使うといいかも • 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) //型不一致
  17. メンバ参照 (bound callable reference) • メンバ(メソッドやプロパティ)を、任意のオブジェクトと結びつけ て参照することができる、関数オブジェクト化することができる class UserService {

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

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

    private val log = LoggerFactory.getLogger("TEST") private fun log(users: List<User>) { users.map(User::toJson).forEach(log::info) } ... } オブジェクト(log)を結びつけた参照 ver 1.1よりサポート
  20. ラムダ式の引数を分解&変数を捨てる • componentN系メソッドを利用した分解宣言 • これが可能なオブジェクトをラムダ式が引数に取るとき、引数 リスト内で分解が可能 listOf( Triple(1, "Java", "Oracle"),

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

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

    Triple(2, "Kotlin", "JetBrains) ) .forEach { (_, name, owner) -> showMessage(name, owner) } 不要な変数(引数)はアンダースコアで受け、捨てる
  23. データクラス、シールドクラス制限緩和 • データクラスが他のクラスを継承することが可能に • シールドクラスを同一ファイル内に定義されたクラスから継承 することが可能に sealed class MyList<out T>

    object Nil: MyList<Nothing>() data class Cons<out T>(val head: T, val tail: MyList<T>): MyList<T>()
  24. 2. KotlinConf 2017

  25. KotlinConf 行ってきました!

  26. KotlinConf 行ってきました! • JetBrains主催のKotlinカンファレンス • 11/2と11/3の2日間 • 会場はサンフランシスコ • 全44セッション、3トラック

    • 1,200枚のチケットは完売 • 日本からは @shiraj_iさんが登壇!
  27. keynoteでマルチプラットフォームが話題に

  28. keynoteでマルチプラットフォームが話題に • マルチプラットフォームでのコードの共有を可 能にする • 例えば ◦ ビジネスロジックやデータ構造などを共有 ▪ 実装だけでなく宣言も共有することが可能

    ◦ ビューやデータアクセス、低レベル処理は各プラット フォーム固有 • 現在はexperimental • JVMとJSをサポート、Nativeはそのうち
  29. サーバサイドKotlin キテます

  30. 3. Kotlin 1.2

  31. Kotlin 1.2で導入・改善された文法や機能 • マルチプラットフォーム・プロジェクト • アノテーション引数における配列リテラル • lateinitプロパティのメタ属性 isInitialized導入 •

    bound callable referenceの改善 • 型推論の改善 • スマートキャストの改善 • その他ツールやライブラリの改善など
  32. マルチプラットフォーム・プロジェクト • マルチプラットフォームでのコードの共有を可能にする • 例えば ◦ ビジネスロジックやデータ構造などを共有 ▪ 実装だけでなく宣言も共有することが可能 ◦

    ビューやデータアクセス、低レベル処理は各プラットフォーム固有 • 現在はexperimental • JVMとJSをサポート、Nativeはそのうち
  33. マルチプラットフォーム・プロジェクトの構造 Common Platform (JVM) Platform (JS) Regular (JVM) Regular (JS)

    矢印は 依存の方向
  34. 実装だけでなく宣言も共有可能 // Common module expect fun hello() // JVM module

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

    actual fun hello() { println("Hello, JVM!") } // JS module actual fun hello() { println("Hello, JS!") }
  36. マルプラ・プロジェクト始め方

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

    @Foo(["hoge", "fuga"]) class Bar
  38. 可変長引数と配列リテラル @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
  39. 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) }
  40. 4. KotlinでAndroid

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

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

    Kotlinには staticもフィールドもない!
  43. コンパニオンオブジェクト class Foo { companion object { val name: String

    = "Foo" } } Foo.name //=> Foo
  44. class Foo { companion object { val name: String =

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

    "Foo" } } Foo.name //=> Foo コンパニオンオブジェクト staticっぽい! フィールドっぽい! →でも違う
  46. JVMと仲良くするアノテーション class Foo { companion object { @JvmField val name:

    String = "Foo" } }
  47. class Foo { companion object { @JvmField val name: String

    = "Foo" } } JVMと仲良くするアノテーション staticフィールドとして見えるようになる
  48. 1.1.4から加わった@Parcelizeを使う • 対象のクラスをいい感じにParcelableにしてくれる • Parcelableインタフェースを実装して • @Parcelizeアノテーションをクラスに付けるだけ

  49. KODEIN • Kotlin製DependencyInjectionライブラリ • kapt不使用

  50. 依存の定義 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<Greeter>("en") with provider { EnglishGreeter() } bind<Greeter>("ja") with singleton { JapaneseGreeter() } bind<Speaker>() with singleton { Human(instance("ja")) } } }
  51. 依存の定義 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<Greeter>("en") with provider { EnglishGreeter() } bind<Greeter>("ja") with singleton { JapaneseGreeter() } bind<Speaker>() with singleton { Human(instance("ja")) } } } インタフェースが1つとその実装が2つある
  52. 依存の定義 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<Greeter>("en") with provider { EnglishGreeter() } bind<Greeter>("ja") with singleton { JapaneseGreeter() } bind<Speaker>() with singleton { Human(instance("ja")) } } } Human has a Greeter
  53. 依存の定義 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<Greeter>("en") with provider { EnglishGreeter() } bind<Greeter>("ja") with singleton { JapaneseGreeter() } bind<Speaker>() with singleton { Human(instance("ja")) } } }
  54. 依存の定義 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<Greeter>("en") with provider { EnglishGreeter() } bind<Greeter>("ja") with singleton { JapaneseGreeter() } bind<Speaker>() with singleton { Human(instance("ja")) } } } 型と実装をバインドする 同一の型であっても名前で区別可能
  55. 依存の定義 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<Greeter>("en") with provider { EnglishGreeter() } bind<Greeter>("ja") with singleton { JapaneseGreeter() } bind<Speaker>() with singleton { Human(instance("ja")) } } } 実装を注入する度に呼び出される
  56. 依存の定義 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<Greeter>("en") with provider { EnglishGreeter() } bind<Greeter>("ja") with singleton { JapaneseGreeter() } bind<Speaker>() with singleton { Human(instance("ja")) } } } シングルトン
  57. 依存の定義 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<Greeter>("en") with provider { EnglishGreeter() } bind<Greeter>("ja") with singleton { JapaneseGreeter() } bind<Speaker>() with singleton { Human(instance("ja")) } } } 実装をそれに依存しない形で注入
  58. 依存の注入 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() } } }
  59. 依存の注入 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() } } } おまじない
  60. 依存の注入 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() } } } 委譲プロパティによる注入
  61. 依存の注入 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() } } } 注入された実装を使うことができる
  62. 依存の注入 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() } } } これを実装すると ちょっとだけ記述量が減る!!
  63. KOIN • Kotlin製DependencyInjectionライブラリ • kapt不使用

  64. 依存の定義 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<Greeter>("en") { EnglishGreeter() } provide<Greeter>("ja") { JapaneseGreeter() } provide<Speaker> { Human(get("ja")) } } override fun onCreate() { super.onCreate() startKoin(this, listOf(module)) } }
  65. 依存の定義 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<Greeter>("en") { EnglishGreeter() } provide<Greeter>("ja") { JapaneseGreeter() } provide<Speaker> { Human(get("ja")) } } override fun onCreate() { super.onCreate() startKoin(this, listOf(module)) } }
  66. 依存の定義 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<Greeter>("en") { EnglishGreeter() } provide<Greeter>("ja") { JapaneseGreeter() } provide<Speaker> { Human(get("ja")) } } override fun onCreate() { super.onCreate() startKoin(this, listOf(module)) } } 毎回生成されるやつ 名前を付けることも可能
  67. 依存の定義 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<Greeter>("en") { EnglishGreeter() } provide<Greeter>("ja") { JapaneseGreeter() } provide<Speaker> { Human(get("ja")) } } override fun onCreate() { super.onCreate() startKoin(this, listOf(module)) } } シングルトン もちろん名前を付けられる
  68. 依存の定義 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<Greeter>("en") { EnglishGreeter() } provide<Greeter>("ja") { JapaneseGreeter() } provide<Speaker> { Human(get("ja")) } } override fun onCreate() { super.onCreate() startKoin(this, listOf(module)) } } 実装をそれに依存しない形で注入
  69. 依存の注入 class MainActivity: AppCompatActivity() { private val speaker: Speaker by

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

    inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... button.setOnClickListener { speaker.speak() } } } 委譲プロパティで注入
  71. 5. KotlinでWeb

  72. Kotlin x Spring • Spring Boot 2 (Spring Framework 5)から、Kotlinフレンド

    リ感がさらにアップ! ◦ Spring WebFluxの追加 ▪ ノンブロッキングでリアクティブなWebフレームワーク ▪ 従来のアノテーションによるハンドラの宣言に加え ▪ ラムダを使用したルーティング ◦ ラムダを使用したBean登録 ◦ KotlinフレンドリなAPI群の追加 • コルーチンの活用
  73. 従来のスタイル @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 } }
  74. 従来のスタイル @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 } }
  75. 従来のスタイル @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 } }
  76. 従来のスタイル @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 } }
  77. 従来のスタイル @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 } }
  78. 従来のスタイル @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 } }
  79. WebFlux @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():

    Mono<String> = 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')
  80. WebFlux @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():

    Mono<String> = 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')
  81. @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): Mono<String>

    = 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')
  82. @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): Mono<String>

    = 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')
  83. @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): Mono<String>

    = 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')
  84. @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): Mono<String>

    = 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')
  85. Reactorでコルーチン @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():

    Mono<String> = 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'
  86. Reactorでコルーチン @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():

    Mono<String> = 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'
  87. Reactorでコルーチン @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():

    Mono<String> = 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'
  88. Reactorでコルーチン @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():

    Mono<String> = 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'
  89. Reactorでコルーチン @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():

    Mono<String> = 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'
  90. ハンドラメソッドをサスペンドに @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'
  91. ハンドラメソッドをサスペンドに @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'
  92. Exposed • Kotlin SQLライブラリ ◦ SQL DSL ◦ Data Access

    Object • JetBrainsが開発 https://github.com/JetBrains/Exposed • 有名なDBMSに対応: ポスグレ, MySQL, Oracle, ...
  93. SQL DSL object Teams: Table() { val id = long("id").autoIncrement().primaryKey()

    val name = varchar("name", length = 100) } fun main(args: Array<String>) { 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) } }
  94. SQL DSL object Teams: Table() { val id = long("id").autoIncrement().primaryKey()

    val name = varchar("name", length = 100) } fun main(args: Array<String>) { 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) } } テーブル定義
  95. SQL DSL object Teams: Table() { val id = long("id").autoIncrement().primaryKey()

    val name = varchar("name", length = 100) } fun main(args: Array<String>) { 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に接続
  96. SQL DSL object Teams: Table() { val id = long("id").autoIncrement().primaryKey()

    val name = varchar("name", length = 100) } fun main(args: Array<String>) { 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) } } トランザクション
  97. SQL DSL object Teams: Table() { val id = long("id").autoIncrement().primaryKey()

    val name = varchar("name", length = 100) } fun main(args: Array<String>) { 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) } } テーブル作成 テーブル削除
  98. SQL DSL object Teams: Table() { val id = long("id").autoIncrement().primaryKey()

    val name = varchar("name", length = 100) } fun main(args: Array<String>) { 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) } } インサート
  99. SQL DSL object Teams: Table() { val id = long("id").autoIncrement().primaryKey()

    val name = varchar("name", length = 100) } fun main(args: Array<String>) { 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) } } 取得
  100. 一対多 関連 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 }
  101. 一対多 関連 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 } 外部キー設定
  102. 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
  103. Data Access Object object Teams: LongIdTable() { val name =

    varchar("name", length = 100) } class Team(id: EntityID<Long>): LongEntity(id) { companion object: LongEntityClass<Team>(Teams) var name by Teams.name } fun main(args: Array<String>) { 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}") } } }
  104. Data Access Object object Teams: LongIdTable() { val name =

    varchar("name", length = 100) } class Team(id: EntityID<Long>): LongEntity(id) { companion object: LongEntityClass<Team>(Teams) var name by Teams.name } fun main(args: Array<String>) { 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}") } } }
  105. Data Access Object object Teams: LongIdTable() { val name =

    varchar("name", length = 100) } class Team(id: EntityID<Long>): LongEntity(id) { companion object: LongEntityClass<Team>(Teams) var name by Teams.name } fun main(args: Array<String>) { 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}") } } }
  106. 一対多 関連 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<Long>): LongEntity(id) { companion object: LongEntityClass<Team>(Teams) var name by Teams.name val tasks by Tasks.referrersOn(Tasks.team) } class Task(id: EntityID<Long>): LongEntity(id) { companion object: LongEntityClass<Task>(Tasks) var content by Tasks.content var completedAt by Tasks.completedAt var team by Team.referencedOn(Tasks.team) }
  107. 一対多 関連 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<Long>): LongEntity(id) { companion object: LongEntityClass<Team>(Teams) var name by Teams.name val tasks by Tasks.referrersOn(Tasks.team) } class Task(id: EntityID<Long>): LongEntity(id) { companion object: LongEntityClass<Task>(Tasks) var content by Tasks.content var completedAt by Tasks.completedAt var team by Team.referencedOn(Tasks.team) } 関連を定義
  108. 取得 Task .find { Tasks.completedAt.isNull() } .forEach { task: Task

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

    -> val team: Team = task.team println("${team.name}: ${task.content}") } N + 1 のおそれ
  110. 無理やりまとめると • コルーチンすごい ◦ Androidはもちろん、サーバサイド(WebFluxなど)でも便利に使える • サーバサイドでもKotlin ◦ KotlinConfでのセッション、多い ◦

    Spring ♡ Kotlin ◦ Exposed...JetBrainsによるKotlin製ORM ◦ (Ktor...JetBrainsによるKotlin製Webマイクロフレームワーク) • 3rd party製 Kotlinライブラリ、フレームワーク ◦ KODEIN ◦ KOIN
  111. メッセージ(再掲) • Kotlinは言語自体も成長途上にある! → 新バージョンをキャッチアップしてきましょう • 活躍の場はAndroidだけじゃない! → サーバサイドも十分現実的な選択肢 →

    JavaScriptやNativeも