みずきち日記

ひらすらプログラミング

Kotlin Fest 2018 に参加した話

f:id:mzkii:20180901212234j:plain

皆さんはじめまして @mzkii です.普段は Kotlin を使って Android アプリ開発をしたり,Python を使って データ分析をしたりしています.

この記事では,8/25(土)に開催された Kotlin Fest 2018スカラシップ枠 として参加しましたので,その様子をレポートしていきたいと思います.

目次

  • 参加するまでの経緯
  • 参加する目的
  • セッション
    • Kotlin で改善する Android アプリの品質
    • Kotlinアプリのリファクタリングポイント
    • start from Convert to Kotlin
    • Kotlin コルーチンを理解しよう
  • LT大会
  • ブースと懇談会
  • おわりに

参加するまでの経緯

このカンファレンスは,DroidKaigi 2018 のオープニングセッションにてサプライズ告知され,自分自身とても楽しみにしていたのですが,ついに7月の頭に開催決定のツイートが流れてきました!!

さらに,8月に入って サイバーエージェント さんが「Kotlin Fest 2018」スカラシップ枠を募集していると Twitter で知り,(既にチケット買っちゃってましたが) ダメ元で応募してみると... 見事参加できることになりました!

Kotlin Fest 2018 における スカラシップ枠 とは,

・Kotlin Fest 2018 のチケット提供(懇親会 有)

・希望者には8月24日(金)の社内見学・技術者とのランチ会に参加いただけます。

・遠方在住の方には宿泊場所を手配。

・交通費支給。

公式ページ より抜粋

となっており,地方学生にとってはとっても嬉しいプランでした😉

また,申し込みには,

・Kotlin Conferenceに参加したい理由を300文字以内で

・githubアカウント

・これでの制作物で一番よく出来たもののURLなど

公式ページ より抜粋

以上3項目を書いて送るだけ!とっても簡単でした!

参加する目的

f:id:mzkii:20180901212021j:plain このカンファレンスは,「Kotlinを愛でる」をビジョンに,Kotlin の知識の共有や,Kotlin を愛してやまない人々に対して交流の場を提供することを目的に開催されました.僕自身,普段から Kotlin は使っていますし,第一線で活躍されているエンジニアの方々と交流を深めながら Kotlin の言語仕様を理解することはもちろん,Android x Kotlin に関する最新技術をキャッチアップしたい!と思って参加しました(特に Kotlin Coroutine).

セッション

早速セッションについてみていきましょう.本当は全部のセッション見たかったのですが,2セッション同時に発表されるので,タイトル見て直感で,面白そう x 明日からすぐに役立ちそう なセッションをピックアップしてみました.

Kotlin で改善する Android アプリの品質

概要

  • なぜ品質の話?

    • 既存の Android アプリケーションを,品質を担保しつつ Java から Kotlin に移行することは,書き直しのコストや,デグレのリスクなどにより難易度が高い.
    • Java --> Kotlin は 上記のようなコストとリスクを上回るメリットがあるのか??
  • Android アプリにおける品質とは?

    • 速い
    • 落ちない
    • 使いやすい
    • 変更しやすい
    • 読みやすい
  • ソフトウェアの品質 = 要因の組み合わせ

    • 内的品質要因 ... スピード,使いやすさなど
    • 外的品質要因 ... モジュール性,読みやすさなど
      • 特に重要な要因は...
        • 正確さ + 頑丈さ = まとめて信頼性と呼ぶ
        • 拡張性 + 再利用性 = まとめてモジュール性と呼ぶ
  • Java で広く実践されている規則は Kotlin だとどうなる??

    • Kotlin では,いたるところに品質要因に関するベストプラクティスが言語仕様的に対応している
    • Java --> Kotlin にすることで,「明瞭で,正しく,再利用可能で,頑強で,柔軟性があり,保守可能なプログラムを書く」エッセンスが自然と取り入れられる.

Kotlin アプリのリファクタリングポイント

概要

  • Gradle Kotlin DSL
    • Gradle のスクリプトを Kotlin で書ける(build.gradle.kts)
    • 補完・ジャンプコードが効くぞい
  • 処理の共通化
    • 処理を共通化するためにどういった選択肢があるのか
      • abstract class
      • interface のデフォルト実装
      • class/property delegation
      • 拡張関数
        • クラスに関数を生やす
        • どこからでも呼び出せるが,スコープを限定したい時は,interface のデフォルト実装が利用できる
      • トップレベル関数
        • ファイルに直接書けるのでどこからでも呼び出せる
        • スコープを限定したい時は,interface に書くと良いが,それすなわち interface のデフォルト実装
  • Mutable を避ける
    • 今どんな値が入っているのか常に考えないといけなくなる
      • 暗黙の前提はバグの温床
    • その var は本当に var にしないといけないのか? (ここの話に使われていた遷移図がめっちゃ分かりやすかった)
      • コンパイル時からずっと不変 --> const val
      • インスタンス生成時に値が確定し不変 --> val
      • ある時点を過ぎると値が確定し不変 --> lazy
        • intent の getExtra などは by lazy で val かつ NonNull化できる
      • ある時点を過ぎると値が確定し不変 && 非Nullable --> lateinit
        • onCreate や onCreateView よりもあとで値が決まる場合は素直に Nullable にしよう
    • その MutableList は本当に MutableList にしないといけないのか?
      • var と MutableList の併用は避ける
        • val x MutableList の組み合わせ
        • var x List の組み合わせ
  • Collection

    • List と MutableList,実体はどっちも ArrayList
    • List は 読み取り専用であり,Immutable ではない
    • Collection の生成は,基本的には listOf,setOf,mapOf
      • 他にも,パフォーマンスを考慮して,setOf,linkedSetOf,hashSetOf,sortedSetOf があるので使うとよいぞ
    • Collection のリファクタリング
      • 2つのリストを同時に使う --> zip
      • 変換と除外 --> map,filter,mapNotNull
      • リストを N 個ずつに分割 --> withIndex でインデックスを付けて,さらに groupBy でグループ分け
      • できるだけ否定「!」は使わない --> all,any,none の活用
  • DSL の拡張

    • Domain Specific Language
    • 内部DSL とも呼ばれ,Kotlin の言語機能で実現可能
    • 構造的なデータ宣言,設定値の記述が簡潔になる
    • AnkoSpek などに使われてる機能らしいが僕にはまだ早かった...
  • Nullability
    • Kotlin といえば Nullability が嬉しい
    • しかし API が Nullable を返した場合,UI 層までのどの層まで Nullable として扱うか問題が出てくる
      • return team?.user?.name?.length
    • null が何を表しているか明確にしよう
      • 滅多に起きないエラーケース --> infra で Exception に変換
      • 任意項目でかつ存在していないを表していることが自明 --> UI 層まで Nullable で渡す
      • 自明じゃない何か --> sealed class を作ってその状態に意味付けする
  • スコープ関数
    • キャプチャする時
      • null 判定時の値が使えるので smart cast が効かないときに有効
      • 単に if 文の代わりにもなる
        • ただし if 文と全く同じなわけではない
    • 処理をまとめたい時
      • Intent の putExtra など
        • ただしローカル変数名との衝突に注意
  • データ構造と状態数
    • 肥大しがちな data class
      • バグの温床なので,ビジネスロジックを考慮して,ありえない状態を避けよう
        • 状態整理 --> Either,Pair
        • 意味付け --> sealed class,data class
      • 状態数を数式に落とし込むと,より厳密に精査できる👀

start from Convert to Kotlin

  • RecyclerView を使ったサンプルアプリを Java --> Kotlin にしながら言語仕様をみてみる
@Parcelize
data class KotlinItem(var id Int = 0, var name String? = null) : Parcelable {
    fun isSameItem(obj Any): Boolean {
        return ((obj as? KotlinItem)?.id == this.id)
    } 
}
KotlinItem(var id Int = 0, var name String? = null)
  • data class で toString などのメソッドを生やす
    • equals
    • hashCode
    • toString
    • copy
data class KotlinItem ...
@Parcelize
data class KotlinItem ...
  • 安全キャスト
    • Any を KotlinItem に変換できるか?
      • キャストできなかったら null を返す
obj as? KotlinItem

Kotlin コルーチンを理解しよう

  • コルーチンとはなにか
    • 一時停止可能な計算インスタンス
    • 特定のスレッドに束縛されない
    • 結果や例外を伴う場合もある
    • 継続状況を持つプログラムが簡単に書けるぞ
      • コールバックスタイルで記述されているとネストが深くなるが...
      • コルーチンだとawaitを使えばキレイに書ける
  • Kotlin ではコルーチンをどうやって実現しているのか
    • コルーチンをステートマシンに変換している
      • 中断と再開を状態遷移と考える
      • もちろん再開に必要な情報もステートマシンに持っておく
      • 逐次内部状態を変えながら実行
    • コルーチン --> ステートマシン
      • ここで出てくるのが suspend修飾子
        • ラムダ式につけるとコルーチン本体
        • 関数につけると中断する場所を表す
      • コルーチンを任意のタイミングで再開するには??
        • 実は,suspend 関数にはコンパイル時に継続インターフェースの引数が生える
        • 継続インターフェースを使って再開処理
        • suspend ラムダはコルーチンの範囲を決める
    • 正直コルーチンを自作するのはしんどい
      • そこで出てくるのがコルーチンビルダー
        • launch(結果を伴わないコルーチンビルダー),async(結果を返すコルーチンビルダー) ...
      • 継続インターセプターで実行スレッドを指定できる
        • CommonPool,JavaFx,UI,Swing
  • Kotlin コルーチンの基本的な使い方
    • まずは launch(結果を伴わないコルーチン)を作成してみる
      • start ... コルーチンの開始方法
      • parent ... 複数のコルーチンを一気にキャンセルしたい場合などに親Jobを指定する
      • onCompletion ... コルーチン終了時に呼び出されるコールバック関数を指定
      • context ... 共有データや継続インターセプター
        • 例えば Android で UI をいじるコルーチンを作る場合は,デフォルトではスレッドプールでコルーチンが走ってしまうので,launch(UI) でコルーチンの処理は常にUIスレッド上で再開するように継続インターセプターを指定する.
    • async / await
      • APIを叩くなど結果を伴うコルーチンを作成したい場合は async を使う
      • async とは,結果を持つコルーチンを作成するコルーチンビルダー関数のこと
      • async { fetchUserInfo(userId) }.await() でAPIを叩く処理は非同期で走らせて,結果は await を使って受け取ることができる
      • launch と async を組み合わせれば大体これで事足りる(そうです)
    • エラーハンドリング
      • 普通に try-catch を使う
    • コルーチンの直列・並列実行
直列実行
val token = async { getToken() } ... ここは中断しない
val profile = async { loadProfile(token.await()) }.await() ... token で結果を待ってから loadProfile を実行する

並列実行
val token = async { getToken() } ... 中断しない
val profile = async { loadProfile() } ... 中断しない
show(token.await(), profile.await()) ... 一気に await して結果待ち
  • キャンセル
    • launch は Job インスタンスを返すので,任意のタイミングで job.cancel() する
    • その時コルーチンは CancellationException を受け取るのでキャンセルしたタイミングで任意の処理を実行することができる
    • まとめて一気にキャンセルしたい場合は,launch の引数に parent があるので,親の Job インスタンス(例えばここでは rootJob)を保持しておいて,launch するタイミングで launch(UI, parent = rootJob) する <-- 結果は UI スレッドで受け取り,親の job を指定する
  • リトライ
    • コルーチン内で,repeat(3) などしておいて,処理が正常終了した場合は return@launch でコルーチンを終了し,なにか Exception が発生した場合に,リトライしなくなかったら return@repeat 等でリトライ処理を抜けるようにする.
    • (読めば理解できるけど,個人的に冗長で読みにくい気がしなくもない...)
  • コルーチンの応用例
    • retrofit ... 結果を Deferred< T > で受け取れる Jake作の Adapter がある
    • EventBus x Cnannel
    • onActivityResult ... android-coroutines を使えば,activity を起動して結果を受け取るまでを一つのコルーチン内で簡潔に書ける

LT大会

LT大会では,8名の方々が発表してくださいました.

一人あたり3分だったので,スピード感ある発表でどれも面白かったです😉

ここでは,スライドが公開されていて,特に印象に残っていた発表を紹介します.

3分で分かる Sequence

  • List では コレクション操作のたびに毎回新しい List を作成する(先行評価)
  • Sequence では 終端操作(toList など)を走らせるまでは処理が実行されない(遅延評価)
  • 素数と操作数が少ない --> List, 多い --> Sequence で使い分けよう

RxJavaを使っている 既存アプリに Kotlin Coroutinesを導入しよう

  • Single<List<Person>>suspend List<Person> に変換してみる
  • ただし,実際のところ Single なメソッドを複数箇所から呼び出していることが多いので,一つだけ置き換えはできない
  • そんなあなたに kotlinx-coroutines-rx2 があるよ
  • Single に await メソッドが生えるので,中断関数に変換できる
  • しかしプロダクションコードでは,テスト,ライフサイクル,エラーハンドリングも必要になってくる
    • この辺りの話は @takahirom さんのこの記事が参考になる

J2K コンバータをカスタマイズする

  • 純正J2Kコンバータが強制的にNonNull変換する問題

    • Convert Java File to Kotlin File を使うと Nullable なコードが NonNull コードに変換されてしまう
      • 親Class,Interface,abstractクラスなどメソッドを継承した場合
      • 引数に @Nullable がない場合
      • Kotlin は NonNull で Java は Nullable 環境になってしまう
  • 解決策

    • 目視でチェック or とりあえず Kotlin 化して動作チェック
    • コンバータカスタマイズしてみよう
      • Kotlin の開発環境構築
        • @shiraji さんの記事が参考になる
      • TypeConverter.kt をカスタマイズ
      • Kotlin のチャイルドIDEA実行
      • レッツコンバー
  • カスタムコンバータを導入してみて

    • hotfix 切ることが激減した
    • コードの信頼感が高まりレビューが簡素化
    • 何より手軽に Java --> Kotlin ができるようになった😉

ブースと懇談会

最後に,展示ブースと懇談会の様子をお届けします.

ブースで面白かったのが,サイバーエージェントさんの Kotlin Puzzlers です.

午前午後と2問ずつ Kotlin に関する問題が出題されていたのですが,

これがめちゃ難問でした(それぞれ1問ずつしか解けなかった😂).

また,ヤフーさんのブースでは黒帯本がもらえる抽選会や,

モブプログラミング教室も開催されており,かなり盛り上がっていました!

写真は黒帯抽選会の様子です

f:id:mzkii:20180901212013j:plain

そしてなんと抽選会当たりました笑(ありがとうございます)

クロージング後はブースやっていた場所で懇談会をやることになりました.

とりあえず写真貼っていきます(肉が美味しかった).

f:id:mzkii:20180901212045j:plain

f:id:mzkii:20180901212035j:plain

f:id:mzkii:20180901212039j:plain

f:id:mzkii:20180901212042j:plain

おわりに

Kotlin Fest 2018 の様子をレポートしてみましたがいかがでしたか? さらに詳しく知りたい方は,#kotlinfestツイッター検索してみると,ここでは紹介しきれなかったセッション資料や,ブース展示の様子などよく分かりますよ!

次回開催はまだ未定とのことですが,今回のイベントは大盛況でしたし,是非来年も開催してほしいです(今度はLT枠などで喋ってみたい).今後も Android x Kotlin のイベントには積極的に参加していくので,僕もイベントを盛り上げられる一員となれるように楽しくやっていきたいと思います!

最後に,Kotlin Fest を開催してくださった運営の皆様,スポンサーの皆様,さらにスカラシップ枠を用意してくださったサイバーエージェント様ありがとうございました.こういったスカラシッププログラムは,地方学生にとっては非常にありがたい内容ですし,今後も継続していただけると,全国の学生の助けになると思います.

ここまでお読み頂きありがとうございました🙇

Kotlin かわいい!

おしまい