Slide 1

Slide 1 text

エバンジェリスト直伝! Kotlinを既存プロダクトで使う 2015-11-28 JJUC CCC 2015 Fall 長澤 太郎 (エムスリー株式会社)

Slide 2

Slide 2 text

もくじ 1. Kotlinとは 2. 環境構築と導入 3. 既存プロダクトにちょい足し 4. がっつりKotlin化 5. 便利なライブラリ 6. まとめ

Slide 3

Slide 3 text

自己紹介/会社紹介

Slide 4

Slide 4 text

自己紹介 ● 長澤 太郎 「たろー」 って呼んでください ● ソフトウェアエンジニア ○ 主にAndroidアプリ開発担当 ○ Android, Java, ときどきRuby, Scala ○ SIerから転職、現在3年目 ● コミュニティ活動 ○ JJUG運営メンバー ○ 日本Kotlinユーザグループ 会長 ● 27歳、蟹座、 と が好き

Slide 5

Slide 5 text

Kotlinとわたし ● 夢中になり始めたのは2012年 (Kotlin歴は3年半以上) ● 以来、ブログや勉強会で情報発信 ● 商業誌、同人誌にも ● Kotlinエバンジェリスト(自称)

Slide 6

Slide 6 text

エムスリー株式会社

Slide 7

Slide 7 text

エムスリー株式会社 インターネットを活用し健康で楽し く 長生きする人を1人でも増や し、不必要な医療コストを1円でも 減らす

Slide 8

Slide 8 text

m3.com ● 医療ポータルサイト ● 医師会員数25万人+ (日本の80%+) ● 医療ニュースや掲示板、 Stack Overflowや SlideShareのようなサービ スも ● その他に医薬品情報、転職 支援、コンシューマ向け サービスなども

Slide 9

Slide 9 text

m3.com Androidアプリ, iOSアプリ

Slide 10

Slide 10 text

もくじ 1. Kotlinとは 2. 環境構築と導入 3. 既存プロダクトにちょい足し 4. がっつりKotlin化 5. 便利なライブラリ 6. まとめ

Slide 11

Slide 11 text

1. Kotlinとは

Slide 12

Slide 12 text

Kotlinとは ● 2011年夏にJetBrainsより発表 ● オープンソース (Apache License 2.0) ● JVM言語、Android公式サポート、altJS ● 静的型付けオブジェクト指向言語

Slide 13

Slide 13 text

Better Java

Slide 14

Slide 14 text

Java大好き♡ ● 高性能・高信頼性のJava仮想マシン ● 進化を続けるプログラミング言語 ○ しかも後方互換性を維持 ● 歴史長い、人口多い ○ ライブラリ・フレームワーク多い ● 何より長く親しんで来た言語なので手になじむ、愛着がある

Slide 15

Slide 15 text

とはいえ、

Slide 16

Slide 16 text

Javaってもんにょりするよね。 そんなにトンガってない ちょうどいい言語が欲しい。

Slide 17

Slide 17 text

もんにょり1: 冗長 MyFavoriteService s = new MyFavoriteService<>(banana);

Slide 18

Slide 18 text

もんにょり1: 冗長 MyFavoriteService s = new MyFavoriteService<>(banana); =の右側からsの型は明らか <>演算子でだいぶ楽になった とは言え後方互換維持のため...

Slide 19

Slide 19 text

Kotlinではこう val s = MyFavoriteService(banana)

Slide 20

Slide 20 text

Kotlinではこう val s = MyFavoriteService(banana) 型推論でスッキリ!

Slide 21

Slide 21 text

もんにょり2: ボイラープレート(定型コード) public final class Fruit { private final String name; private final int price; private final Country country; public Fruit(final String name, final int price, final Country country) { this.name = name; this.price = price; this.country = country; } public String getName() { return name; } public int getPrice() { return price; } public Country getCountry() { return country; } public Fruit withName(final String name) { return new Fruit(name, price, country); } public Fruit withPrice(final int price) { return new Fruit(name, price, country); } public Fruit withCountry(final Country country) { return new Fruit(name, price, country); } // toStringとか // equalsとか // hashCodeとか public static Builder builder() { return new Builder(); } public static class Builder { private String name; private int price; private Country country; public Fruit build() { return new Fruit(name, price, country); } public Builder name(final String name) { this.name = name; return this; } public Builder price(final int price) { this.price = price; return this; } public Builder country(final Country country) { this.country = country; return this; } ● データを表現するクラス ● イミュータブルにしたい ● private final フィールド ● コンストラクタ ● getter ● toString, equals, hashCode ● Builderクラス (Lombokライクな) Fruitクラス

Slide 22

Slide 22 text

Kotlinではこう data class Fruit( val name :String, val price :Int, val country :Country )

Slide 23

Slide 23 text

Kotlinではこう data class Fruit( val name :String, val price :Int, val country :Country ) コンストラクタ引数が プロパティに! data修飾子で toStringなどを 自動生成

Slide 24

Slide 24 text

もんにょり3: ぬるぽ String s = null; s.toUpperCase();

Slide 25

Slide 25 text

もんにょり3: ぬるぽ String s = null; s.toUpperCase(); NullPointerException nullチェックすれば?

Slide 26

Slide 26 text

nullなのか nullじゃないのか わからない

Slide 27

Slide 27 text

Java的な工夫 @Nonnull String reverse(String s) {…} Optional initial(String s) {…}

Slide 28

Slide 28 text

誰にもnullは止められない!! @Nonnull Optional findFoo() { return null; }

Slide 29

Slide 29 text

Kotlinではこう val s1: String = null // NG val s2: String? = null // OK s2.toUpperCase() // NG if(s2 != null) { s2.toUpperCase() // OK } s2?.toUpperCase() // OK

Slide 30

Slide 30 text

Kotlinではこう val s1: String = null // NG val s2: String? = null // OK s2.toUpperCase() // NG if(s2 != null) { s2.toUpperCase() // OK } s2?.toUpperCase() // OK nullか否かを 明確に区別し、 扱いも厳しい

Slide 31

Slide 31 text

Kotlin その他の特徴 ● 型安全、配列専用の構文はない。クラス定義時に型パラメータに対 して変位指定ができる。 ● 拡張関数: 静的に、既存の型にメソッドを生やせるようなやつ ● 高階関数、関数オブジェクト、ラムダ式 ● 言語機能としての実装の委譲サポート ● プロパティの委譲 ● Javaとの相互運用性が非常に高い ● Javaと同様のコンパイル速度 (目標)

Slide 32

Slide 32 text

Kotlin Javaってもんにょりするよね。 そんなにトンガってない ちょうどいい言語が欲しい。

Slide 33

Slide 33 text

まとめ ● Java(JVM)が好きで、なくては生きていけない ● とはいえJavaの文法とか機能とか... ● Kotlinという選択肢 ● JetBrains製 静的型付けオブジェクト指向言語 ● Null-Safetyなどのユニークな機能 ● マイルド、現実見てる感

Slide 34

Slide 34 text

2. 環境構築と導入

Slide 35

Slide 35 text

環境構築 1. JDKを入れる (Kotlin的には1.6+)

Slide 36

Slide 36 text

環境構築 1. JDKを入れる (Kotlin的には1.6+) 2. IntelliJ IDEA 15を入れる (有償版・無償版どちらでも)

Slide 37

Slide 37 text

環境構築 1. JDKを入れる (Kotlin的には1.6+) 2. IntelliJ IDEA 15を入れる (有償版・無償版どちらでも) 以上 ※必要に応じてプラグインのアップデートをすれば完璧

Slide 38

Slide 38 text

導入 ● 前提 ○ 既存Javaプロジェクト ○ Gradle 1. おもむろにsrc/main/javaにHelloWorld.ktファイルを作る 2. 「as Kotlin(Gradle) module」をクリックする 3. 流れに身をまかせる 4. build.gradleにKotlinの設定が自動的に追記される ※Android Studioもだいたい同じ ※src/main/kotlinディレクトリ作るといいよ

Slide 39

Slide 39 text

デモ

Slide 40

Slide 40 text

Hello World package sample; class HelloWorld { companion object { @JvmStatic fun main(args: Array): Unit { System.out.println("Hello, world"); } } }

Slide 41

Slide 41 text

Hello World package sample; class HelloWorld { companion object { @JvmStatic fun main(args: Array): Unit { System.out.println("Hello, world"); } } } 普通こんな コード書きません

Slide 42

Slide 42 text

Hello World package sample fun main(args: Array) { println("Hello, world") }

Slide 43

Slide 43 text

Hello World package sample fun main(args: Array) { println("Hello, world") } パッケージ直下に関数 println Kotlin標準API 変数の型は後置 配列は ジェネリクス Unitのときは 省略可 セミコロン 不要 funで 関数定義

Slide 44

Slide 44 text

ちなみに... package sample; class HelloWorld { companion object { @JvmStatic fun main(args: Array): Unit { System.out.println("Hello, world"); } } }

Slide 45

Slide 45 text

ちなみに... package sample; class HelloWorld { companion object { @JvmStatic fun main(args: Array): Unit { System.out.println("Hello, world"); } } } Javaとの親和性その1 Kotlinにはstaticは存在しない が、Javaからの見え方を意識して いる。

Slide 46

Slide 46 text

まとめ ● 環境構築は簡単 ● 開発元がJetBrainsというだけありIDEAでのサポートが厚い ● HelloWorld スッキリしたコードだが見慣れた雰囲気を感じる ● アノテーションでJavaに歩み寄る

Slide 47

Slide 47 text

3. 既存プロダクトにちょい足し

Slide 48

Slide 48 text

ちょい足しのススメ ● JavaとKotlinの共存が簡単 ● リスクが小さい ● 例えばテストコードをKotlinで ● 例えばデータクラスをKotlinで ○ 特におすすめ ○ ロジックがないのでKotlin初心者でも安心

Slide 49

Slide 49 text

Kotlinのクラス class Id(value: Long) { private val _value: Long = value fun getValue(): Long = _value override fun toString(): String = "Id($_value)" } val id = Id(123) id.getValue() //=> 123 id.toString() //=> "Id(123)"

Slide 50

Slide 50 text

Kotlinのクラス class Id(value: Long) { private val _value: Long = value fun getValue(): Long = _value override fun toString(): String = "Id($_value)" } val id = Id(123) id.getValue() //=> 123 id.toString() //=> "Id(123)" コンストラクタ インスタンス生成

Slide 51

Slide 51 text

Kotlinのクラス class Id(value: Long) { private val _value: Long = value fun getValue(): Long = _value override fun toString(): String = "Id($_value)" } val id = Id(123) id.getValue() //=> 123 id.toString() //=> "Id(123)" 値を保持し、 getterを提供 メソッド呼び出し デフォルトは public

Slide 52

Slide 52 text

Kotlinのクラス class Id(value: Long) { private val _value: Long = value fun getValue(): Long = _value override fun toString(): String = "Id($_value)" } val id = Id(123) id.getValue() //=> 123 id.toString() //=> "Id(123)" toStringを オーバライド 式展開

Slide 53

Slide 53 text

プロパティ class Id(value: Long) { val value: Long = value override fun toString(): String = "Id($value)" } val id = Id(123) id.value //=> 123 id.toString() //=> "Id(123)"

Slide 54

Slide 54 text

プロパティ class Id(value: Long) { val value: Long = value override fun toString(): String = "Id($value)" } val id = Id(123) id.value //=> 123 id.toString() //=> "Id(123)" フィールドに似ているが、 内部状態は隠蔽されている

Slide 55

Slide 55 text

コンストラクタ引数でプロパティ定義 class Id(val value: Long) { override fun toString(): String = "Id($value)" } val id = Id(123) id.value //=> 123 id.toString() //=> "Id(123)"

Slide 56

Slide 56 text

データクラス data class Id(val value: Long) val id = Id(123) id.value //=> 123 id.toString() //=> "Id(value=123)"

Slide 57

Slide 57 text

データクラス data class Id(val value: Long) val id = Id(123) id.value //=> 123 id.toString() //=> "Id(value=123)" data修飾子 本体ブロックなし (optional) toStringなどの実装が 提供される

Slide 58

Slide 58 text

m3.comアプリでやってみる ● 対象データ ○ 「臨床ダイジェスト」における「論文 (Thesis)」 ○ 論文に紐付いた疾患 (Disease) ● 技術的な制約 ○ JSONからJacksonを使ってデシリアライズする ○ JSONの構造が微妙... ○ Javaから使用することを意識する

Slide 59

Slide 59 text

疾患 data class Disease( val id: String, val name: String ): Serializable

Slide 60

Slide 60 text

論文 - プライマリコンストラクタ @JsonIgnoreProperties(ignoreUnknown = true) data class Thesis( val id: String, val title: String, @get:JvmName("isRead") val read: Boolean, val disease: Disease ): Serializable { ...

Slide 61

Slide 61 text

論文 - プライマリコンストラクタ @JsonIgnoreProperties(ignoreUnknown = true) data class Thesis( val id: String, val title: String, @get:JvmName("isRead") val read: Boolean, val disease: Disease ): Serializable { ... そのままだと JavaからはgetRead() get: ゲッターにアノテーション付加 JvmNameアノテーション Java用の名前指定

Slide 62

Slide 62 text

論文 - セカンダリコンストラクタ ... constructor( @JsonProperty("id") id: String, @JsonProperty("title") title: String, @JsonProperty("read") read: Boolean, @JsonProperty("diseaseId") diseaseId: String @JsonProperty("diseaseName") diseaseName: String ): this( id = id, title = title, read = read, disease = Disease( id = diseaseId, name = diseaseName ) ) ...

Slide 63

Slide 63 text

論文 - メソッド定義 ... fun withRead(read: Boolean) = copy(read = read) }

Slide 64

Slide 64 text

論文 - メソッド定義 ... fun withRead(read: Boolean) = copy(read = read) } dataアノテーションに より自動生成される copyメソッド 名前付き引数 Java用にLombokライクな メソッドを用意

Slide 65

Slide 65 text

まとめ ● リスクヘッジのため、まずはちょい足し ● データクラスがおすすめ ● プロパティ便利 ● dataアノテーション便利 ● アノテーションでJavaに歩み寄る パート2 ● 補助的なメソッドを提供してJavaフレンドリに

Slide 66

Slide 66 text

4. がっつりKotlin化

Slide 67

Slide 67 text

Kotlinの機能を俯瞰 ● dataクラス ● プロパティ ● Null-Safety ● 高階関数、関数オブジェクト、ラムダ式 ● if-else, for, when, try-catch 制御構文 ● 拡張関数 ● プロパティの委譲 (Delegated Property)

Slide 68

Slide 68 text

Kotlinの機能を俯瞰 ● dataクラス ● プロパティ ● Null-Safety ● 高階関数、関数オブジェクト、ラムダ式 ● if-else, for, when, try-catch 制御構文 ● 拡張関数 ● プロパティの委譲 (Delegated Property) まあ普通

Slide 69

Slide 69 text

Kotlinの機能を俯瞰 ● dataクラス ● プロパティ ● Null-Safety ● 高階関数、関数オブジェクト、ラムダ式 ● if-else, for, when, try-catch 制御構文 ● 拡張関数 ● プロパティの委譲 (Delegated Property) 便利だが クセがある

Slide 70

Slide 70 text

拡張関数と Delegated Propertyを いかに使いこなすか Kotlinプログラミングのコツ ※一エバンジェリストの主観です

Slide 71

Slide 71 text

拡張関数 ● 既存のクラスやインタフェースに変更を加えないで、 メソッドを追加したように見せる機能 fun String.hello() { println("Hello, $this!") } "world".hello()

Slide 72

Slide 72 text

Java用ライブラリをいい感じに 例えばApache Commons Lang: // normal StringUtils.reverse("hoge") //=> egoh // extension function fun String.reverse() = StringUtils.reverse(this) "hoge".reverse() //=> egoh

Slide 73

Slide 73 text

特にAndroidでは真価を発揮 // this: Activity or Context etc Toast.makeText(this, "Hello", Toast.LENGTH_SHORT).show() fun Context.toast(message: String, duration: Int = Toast.LENGTH_SHORT) { Toast.makeText(this, message, duration).show() } toast("Hello")

Slide 74

Slide 74 text

特にAndroidでは真価を発揮 // this: Activity or Context etc Toast.makeText(this, "Hello", Toast.LENGTH_SHORT).show() fun Context.toast(message: String, duration: Int = Toast.LENGTH_SHORT) { Toast.makeText(this, message, duration).show() } toast("Hello") 引数にContextを取る ようなメソッドが多い デフォルト引数

Slide 75

Slide 75 text

m3.comアプリでやってみる ● Jacksonを用いたデシリアライズ // Java public enum ObjectMapperUtils { public static Optional read(ObjectMapper objectMapper, String json, Class clazz) { try { return Optional.of(objectMapper.readValue(json, clazz)); } catch(Exception e) { return Optional.absent(); } } } ~~~~~~~~ Optional thesis = ObjectMapperUtils.read(objectMapper, json, Thesis.class);

Slide 76

Slide 76 text

ObjectMapperの拡張関数を定義 inline fun ObjectMapper.read(json: String): T? = try { readValue(json, T::class.java) } catch(e: Exception) { null } val thesis: Thesis? = objectMapper.read(json) // val thesis = objectMapper.read(json)

Slide 77

Slide 77 text

ObjectMapperの拡張関数を定義 inline fun ObjectMapper.read(json: String): T? = try { readValue(json, T::class.java) } catch(e: Exception) { null } val thesis: Thesis? = objectMapper.read(json) // val thesis = objectMapper.read(json) reified: 型パラ メータの具体化 AnyでTの上限指定。 デフォはAny? reifiedの場合は inline化必須 Classが 取得できる

Slide 78

Slide 78 text

ObjectMapperの拡張関数を定義 inline fun ObjectMapper.read(json: String): T? = try { readValue(json, T::class.java) } catch(e: Exception) { null } val thesis: Thesis? = objectMapper.read(json) // val thesis = objectMapper.read(json)

Slide 79

Slide 79 text

Delegated Property ● プロパティへのアクセスを別のオブジェクトに委譲する仕組み class Greeter { val message: String by lazy { "Hello, world" } } 標準ライブラリで提供さ れている関数 lazy

Slide 80

Slide 80 text

ユーザ定義Delegated Property class MyProp(initial: T) { private var value: T = initial operator fun getValue(thisRef: Any, prop: KProperty<*>): T { return value } operator fun setValue(thisRef: Any, prop: KProperty<*>, value: T) { this.value = value } } class User(name: String) { var name: String by MyProp(name) }

Slide 81

Slide 81 text

m3.comアプリでやってみる ● インテントから論文を受け取る // Java public class ThesisActivity extends Activity { Optional thesis = Optional.absent(); protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState) Serializable t = getIntent() .getExtras() .getSerialize("thesis"); thesis = (t != null) ? Optional.of((Thesis) t) : Optional.absent(); } }

Slide 82

Slide 82 text

Delegated Propertyでスッキリ fun Activity.extra() = object: ReadOnlyProperty { override fun getValue(thisRef: Activity, prop: KProperty<*>): T? = intent.extras.getSerializable(prop.name) as? T } class ThesisActivity: Activity() { val thesis: Thesis? by extra() override fun onCreate(savedInstanceState: Bundle?) { Log.d("TEST", thesis?.title) } } ※実用にはもう少し改良が必要

Slide 83

Slide 83 text

まとめ ● 拡張関数 ○ 既存の型にメソッドを追加するイメージ ● Delegated Property ○ プロパティへのアクセスを他オブジェクトに委譲 ● 便利なので上手く使おう!乱用注意

Slide 84

Slide 84 text

5. 便利なライブラリ

Slide 85

Slide 85 text

便利なライブレリ・フレームワーク ● KotterKnife ● RxAndroid ● Dagger2 ● Realm

Slide 86

Slide 86 text

KotterKnife ● View bindingライブラリ ● Butter KnifeのKotlin版 class ThesisView(context: Context): FrameLayout(context) { val titleView: TextView by bindView(R.id.title) fun set(thesis: Thesis) { titleView.text = thesis.title } }

Slide 87

Slide 87 text

RxAndroid ● RxJava + Android固有の機能 client.getTheses() .onErrorReturn { listOf() } .observeOn(AndroidSchedulers.mainThread()) .subscribe { theses -> thesesAdapter.theses = theses }

Slide 88

Slide 88 text

Dagger2 ● DIフレームワーク、Annotation Processingで ● kaptで動く: Kotlin Annotation Processing Tool ● lateinit修飾子で初期化の遅延を明示しNullable対策 class ThesisListActivity { @Inject lateinit var client: Client fun onCreate(savedInstanceState: Bundle?) { // (省略) M3ComApp.component(this).inject(this) client.getTheses() } }

Slide 89

Slide 89 text

Realm ● モバイルデータベース (とそのAPI群) ● kaptで動く val t = Realm.getInstance(context).use { realm -> realm.where(Thesis::class.java) .equalTo("read", false) .findFirst() } Closeableの拡張関数 ローンパターン

Slide 90

Slide 90 text

6. まとめ

Slide 91

Slide 91 text

● Javaはいい言語、JVMもいい環境 ● 少なからずもんにょり感はある、特にAndroidでは

Slide 92

Slide 92 text

● Javaはいい言語、JVMもいい環境 ● 少なからずもんにょり感はある、特にAndroidでは ● ちょうどいい言語が欲しい→それKotlin ● 見たことがあるような文法 ● Null-Safety、ラムダ式、型推論...便利そうだ!

Slide 93

Slide 93 text

● Javaはいい言語、JVMもいい環境 ● 少なからずもんにょり感はある、特にAndroidでは ● ちょうどいい言語が欲しい→それKotlin ● 見たことがあるような文法 ● Null-Safety、ラムダ式、型推論...便利そうだ! ● まずはデータクラスでちょい足し ● 拡張関数とDelegated Propertyを駆使してがっつりKotlin ● Java向けKotlin向け問わず便利なライブラリ・FWを使う

Slide 94

Slide 94 text

Kotlin かわいいよ、Kotlin

Slide 95

Slide 95 text

at.m3.com/job 一緒にKotlinしましょう! Kotlin + Android Kotlin + Spring Boot etc...