Slide 1

Slide 1 text

start from Convert to Kotlin @mochico 2018.8.25 Kotlin Fest 2018

Slide 2

Slide 2 text

About me mochico(@_mochicon_) Android / Java / Kotlin Techbooster / 技術書典

Slide 3

Slide 3 text

みなさんKotlin愛でてますか?

Slide 4

Slide 4 text

想定プロジェクト • サービス開始から数年たったコード • 無数のデータクラス • テストはほとんどナシ • そのときどきのベスト・プラクティス

Slide 5

Slide 5 text

メンバーが増えていろんなコードスタイルの⼈がいる ボイラープレートなコードが多い 引数が多くて呼び出しかたが複雑

Slide 6

Slide 6 text

解決できる

Slide 7

Slide 7 text

で!

Slide 8

Slide 8 text

今⽇の⽬的 • まだあまりKotlinしてない⼈:帰ったらKotlinして みたくなる • ちょっとKotlinしてる⼈:もっとKotlinの良さを享 受する • だいぶKotlinしてる⼈:もっといい⽅法があったら 教えてください!

Slide 9

Slide 9 text

Kotlinのよいところ • 静的型付き⾔語で型安全! • Null安全! • 副作⽤のない関数型で書けるのでエンバグしにくい • ラムダでシンプルに書ける • APIが豊富でやりたいことが⾃由に表現できる • Javaとの完全互換

Slide 10

Slide 10 text

本当に?

Slide 11

Slide 11 text

⾃分のKotlin導⼊遍歴 • 開始3年のサービス -> Testに導⼊ • 開始1年のサービス -> 新しく作る部分に導⼊ • 開始5年のサービス -> 古い部分を積極的に置き換え

Slide 12

Slide 12 text

Kotlinを積極的に導⼊しなかったケース • Javaのコードで不⾜がない • 変更の予定がない(少ない) • Javaでなければならない理由がある

Slide 13

Slide 13 text

Kotlinにしない理由がない

Slide 14

Slide 14 text

もっとAndroidアプリで上⼿ にKotlinするには?

Slide 15

Slide 15 text

Java製アプリにKotlinを導⼊する 1. テストを書く 2. 新しく書く部分で導⼊する 3. 既存のJavaコードをConvert

Slide 16

Slide 16 text

Convert Java File to Kotlin File

Slide 17

Slide 17 text

Convert Java File to Kotlin File

Slide 18

Slide 18 text

SampleApp

Slide 19

Slide 19 text

MainActivity.java public class JavaMainActivity extends AppCompatActivity { RecyclerView recyclerView; ItemAdapter adapter = new ItemAdapter(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(view, "tapped!", Snackbar.LENGTH_LONG) .setAction("Action", null).show(); } }); recyclerView = findViewById(R.id.recycler_view); recyclerView.setAdapter(adapter); } }

Slide 20

Slide 20 text

public class JavaItem implements Parcelable { public JavaItem(int id, String name) { this.id = id; this.name = name; } private int id; private String name; public int getId() { return id; } public String getName() { return name; } public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } private JavaItem(Parcel source) { id = source.readInt(); name = source.readString(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int atags) { dest.writeInt(id); dest.writeString(name); } public static final Creator CREATOR = new Creator() { @Override public JavaItem createFromParcel(Parcel source) { return new JavaItem(source); } @Override public JavaItem[] newArray(int size) { return new JavaItem[size]; } }; public boolean isSameItem(Object obj) { return obj instanceof JavaItem && ((JavaItem) obj).getId() == this.id; } } Item.java

Slide 21

Slide 21 text

Androidアプリで便利なKotlinの機能 • プロパティ • Null安全 • data class • Kotlin Android Extension • イミュータブル

Slide 22

Slide 22 text

Convertしたコードから もっとKotlinしてみよう!

Slide 23

Slide 23 text

データクラスを Kotlinで書き換える

Slide 24

Slide 24 text

public class JavaItem implements Parcelable { public JavaItem(int id, String name) { this.id = id; this.name = name; } private int id; private String name; public int getId() { return id; } public String getName() { return name; } public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } private JavaItem(Parcel source) { id = source.readInt(); name = source.readString(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int atags) { dest.writeInt(id); dest.writeString(name); } public static final Creator CREATOR = new Creator() { @Override public JavaItem createFromParcel(Parcel source) { return new JavaItem(source); } @Override public JavaItem[] newArray(int size) { return new JavaItem[size]; } }; public boolean isSameItem(Object obj) { return obj instanceof JavaItem && ((JavaItem) obj).getId() == this.id; } } Item.java

Slide 25

Slide 25 text

Convert to Kotlin !

Slide 26

Slide 26 text

class KotlinItem : Parcelable { var id: Int = 0 var name: String? = null constructor(id: Int, name: String) { this.id = id this.name = name } private constructor(source: Parcel) { id = source.readInt() name = source.readString() } public override fun describeContents(): Int { return 0 } public override fun writeToParcel(dest: Parcel, atags: Int) { dest.writeInt(id) dest.writeString(name) } fun isSameItem(obj: Any): Boolean { return (obj is KotlinItem && (obj as KotlinItem).id == this.id) } companion object { val CREATOR: Parcelable.Creator = object : Parcelable.Creator { public override fun createFromParcel(source: Parcel): KotlinItem { return KotlinItem(source) } public override fun newArray(size: Int): Array { return arrayOfNulls(size) } } } } Item.kt

Slide 27

Slide 27 text

プロパティ • Kotlinではフィールドとアクセサメソッドの組合せ • プロパティにアクセスすると実際にはアクセサが使 ⽤される

Slide 28

Slide 28 text

実際にはどんなコードになっているのか?

Slide 29

Slide 29 text

Decompile

Slide 30

Slide 30 text

Decompile

Slide 31

Slide 31 text

Decompile

Slide 32

Slide 32 text

Decompile

Slide 33

Slide 33 text

class KotlinItem : Parcelable { var id: Int = 0 var name: String? = null constructor(id: Int, name: String) { this.id = id this.name = name } private constructor(source: Parcel) { id = source.readInt() name = source.readString() } public override fun describeContents(): Int { return 0 } public override fun writeToParcel(dest: Parcel, atags: Int) { dest.writeInt(id) dest.writeString(name) } fun isSameItem(obj: Any): Boolean { return (obj is KotlinItem && (obj as KotlinItem).id == this.id) } companion object { val CREATOR: Parcelable.Creator = object : Parcelable.Creator { public override fun createFromParcel(source: Parcel): KotlinItem { return KotlinItem(source) } public override fun newArray(size: Int): Array { return arrayOfNulls(size) } } } } Item.kt

Slide 34

Slide 34 text

プライマリコンストラクタと デフォルト引数

Slide 35

Slide 35 text

プライマリコンストラクタ class KotlinItem( var id: Int, var name: String? )

Slide 36

Slide 36 text

デフォルト引数 class KotlinItem( var id: Int = 0, var name: String? = null ) val item = KotlinItem(1)

Slide 37

Slide 37 text

class KotlinItem( var id: Int = 0, var name: String? = null ) : Parcelable { private constructor(source: Parcel) : this(source.readInt(),source.readString()) public override fun describeContents(): Int { return 0 } public override fun writeToParcel(dest: Parcel, atags: Int) { dest.writeInt(id) dest.writeString(name) } fun isSameItem(obj: Any): Boolean { return (obj is KotlinItem && (obj as KotlinItem).id == this.id) } companion object { val CREATOR: Parcelable.Creator = object : Parcelable.Creator { public override fun createFromParcel(source: Parcel): KotlinItem { return KotlinItem(source) } public override fun newArray(size: Int): Array { return arrayOfNulls(size) } } } } Item.kt

Slide 38

Slide 38 text

data class

Slide 39

Slide 39 text

data class in Kotlin data class Item (val id: Int, val name: String)

Slide 40

Slide 40 text

data classで⾃動⽣成されるメソッド • equals • hashCode • toString • copy

Slide 41

Slide 41 text

Item.java#toString() Item.toString: mochico.example.com.start.from.convert.to.kotlin.models.JavaItem@98824bc

Slide 42

Slide 42 text

Item.kt#toString() Item.toString: Item(id=1, name=name)

Slide 43

Slide 43 text

Item.kt data class KotlinItem( var id: Int = 0, var name: String? = null ) : Parcelable { private constructor(source: Parcel) : this(source.readInt(), source.readString()) public override fun describeContents(): Int { return 0 } public override fun writeToParcel(dest: Parcel, atags: Int) { dest.writeInt(id) dest.writeString(name) } fun isSameItem(obj: Any): Boolean { return (obj is KotlinItem && (obj as KotlinItem).id == this.id) } companion object { val CREATOR: Parcelable.Creator = object : Parcelable.Creator { public override fun createFromParcel(source: Parcel): KotlinItem { return KotlinItem(source) } public override fun newArray(size: Int): Array { return arrayOfNulls(size) } } } }

Slide 44

Slide 44 text

Parcelable

Slide 45

Slide 45 text

Kotlin Android Extensions apply plugin: 'kotlin-android-extensions' androidExtensions { experimental = true }

Slide 46

Slide 46 text

@Parcelize @Parcelize data class Item( var id: Int = 0, var name: String? = null ) : Parcelable

Slide 47

Slide 47 text

Decompile @Parcelize public static final android.os.Parcelable.Creator CREATOR = new Item.Creator(); public final int describeContents() { return 0; } public final void writeToParcel(@NotNull Parcel parcel, int flags) { Intrinsics.checkParameterIsNotNull(parcel, "parcel"); parcel.writeInt(this.id); parcel.writeString(this.name); } @Metadata( mv = {1, 1, 10}, bv = {1, 0, 2}, k = 3 ) public static class Creator implements android.os.Parcelable.Creator { @NotNull public final Object[] newArray(int size) { return new Item[size]; } @NotNull public final Object createFromParcel(@NotNull Parcel in) { Intrinsics.checkParameterIsNotNull(in, "in"); return new Item(in.readInt(), in.readString()); } }

Slide 48

Slide 48 text

Item.kt @Parcelize data class Item( var id: Int = 0, var name: String? = null ) : Parcelable { fun isSameItem(obj: Any): Boolean { return (obj is KotlinItem && (obj as KotlinItem).id == this.id) } }

Slide 49

Slide 49 text

安全キャスト • キャストできない場合にnullを返す

Slide 50

Slide 50 text

Item.kt @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) } }

Slide 51

Slide 51 text

データクラスをつかう

Slide 52

Slide 52 text

ぬるぽ

Slide 53

Slide 53 text

ガッ!!

Slide 54

Slide 54 text

Null安全 ? • 予期しないNullはユーザーのクラッシュに直結する • nullが来る可能性がある場⾯が多くある • どの場⾯でnullが来るかを明確にしNPEの可能性を 下げる

Slide 55

Slide 55 text

Null許容型とNull⾮許容型

Slide 56

Slide 56 text

プラットフォーム型 ! • Javaの型はNull許容についての情報がないものとし て扱う • 警告などで⾒られるが、コードで定義はできない • @Nullable, @Nonull アノテーションをつけたとき はNull許容型,⾮許容型として扱う

Slide 57

Slide 57 text

Null許容型にアクセスする

Slide 58

Slide 58 text

⾮null表明 !! val item: Item? = null item!!.id item.name

Slide 59

Slide 59 text

⾮null表明 !! val item: Item? = null item!!.id item.name

Slide 60

Slide 60 text

Nullを安全に回避する • smart cast • ?: エルビス演算⼦ • ?.

Slide 61

Slide 61 text

Smart cast val item: Item? = null if (item == null) { return } item.id item.name

Slide 62

Slide 62 text

エルビス演算⼦ val item: Item? = null item ?: return item.id item.name

Slide 63

Slide 63 text

Nullable in Test

Slide 64

Slide 64 text

?. val javaItem: Item? = null javaItem?.id javaItem?.name

Slide 65

Slide 65 text

Nullを安全に回避する • どれを使⽤するかはケースバイケース • !! が本当に必要なことはあまりない

Slide 66

Slide 66 text

named argument val item1 = Item(id = 1, name = "name") val item2 = Item(name = "name", id = 1)

Slide 67

Slide 67 text

ActivityをKotlinに書き換える

Slide 68

Slide 68 text

MainActivity.java public class JavaMainActivity extends AppCompatActivity { RecyclerView recyclerView; ItemAdapter adapter = new ItemAdapter(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(view, "tapped!", Snackbar.LENGTH_LONG) .setAction("Action", null).show(); } }); recyclerView = findViewById(R.id.recycler_view); recyclerView.setAdapter(adapter); } }

Slide 69

Slide 69 text

Convert to Kotlin !

Slide 70

Slide 70 text

Converted MainActivity.kt

Slide 71

Slide 71 text

Converted MainActivity.kt

Slide 72

Slide 72 text

可視修飾⼦

Slide 73

Slide 73 text

可視修飾⼦ • public:どこからも参照可能 • internal :モジュール内から のみ参照可能 • private:クラス内からのみ 参照可能 • なし:public • public:どこからも参照可能 • protected:現在のクラスとサ ブクラスからアクセスできる • private:クラス内からのみ参 照可能 • なし:同じパッケージのクラ スから参照可能 Java Kotlin

Slide 74

Slide 74 text

Converted MainActivity.kt

Slide 75

Slide 75 text

どこでどう初期化するか?

Slide 76

Slide 76 text

var & val var variableNumber = 1 variableNumber = 2 // OK val valueNumber = 1 valueNumber = 2 // ίϯύΠϧΤϥʔ

Slide 77

Slide 77 text

private var recyclerView: RecyclerView? = null

Slide 78

Slide 78 text

private var recyclerView: RecyclerView? = null

Slide 79

Slide 79 text

lateinit class KotlinMainActivity : AppCompatActivity() { private lateinit var recyclerView: RecyclerView private var adapter = ItemAdapter() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) recyclerView = findViewById(R.id.recycler_view)

Slide 80

Slide 80 text

by lazy private val recyclerView: RecyclerView by lazy { findViewById(R.id.recycler_view) }

Slide 81

Slide 81 text

MainActivity.kt

Slide 82

Slide 82 text

ラムダ • 他の関数に渡すことができる無名関数 • AndroidではOnClickListenerなどでよくみる

Slide 83

Slide 83 text

MainActivity.kt class MainActivity : AppCompatActivity() { private lateinit var recyclerView: RecyclerView private var adapter = ItemAdapter() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val toolbar = findViewById(R.id.toolbar) setSupportActionBar(toolbar) findViewById(R.id.fab).setOnClickListener { view -> Snackbar.make(view, "tapped!", Snackbar.LENGTH_LONG) .setAction("Action", null).show() } recyclerView = findViewById(R.id.recycler_view) recyclerView.adapter = adapter } }

Slide 84

Slide 84 text

Convertしたコードの おさらい

Slide 85

Slide 85 text

Java entity class public class JavaItem implements Parcelable { public JavaItem(int id, String name) { this.id = id; this.name = name; } private int id; private String name; public int getId() { return id; } public String getName() { return name; } public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } private JavaItem(Parcel source) { id = source.readInt(); name = source.readString(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int atags) { dest.writeInt(id); dest.writeString(name); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public JavaItem createFromParcel(Parcel source) { return new JavaItem(source); } @Override public JavaItem[] newArray(int size) { return new JavaItem[size]; } }; }

Slide 86

Slide 86 text

Kotlin data class @Parcelize data class Item( var id: Int = 0, var name: String? = null ) : Parcelable { fun isSameItem(obj: Any): Boolean { return (obj as KotlinItem).id == this.id } }

Slide 87

Slide 87 text

MainActivity.java public class JavaMainActivity extends AppCompatActivity { RecyclerView recyclerView; ItemAdapter adapter = new ItemAdapter(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(view, "tapped!", Snackbar.LENGTH_LONG) .setAction("Action", null).show(); } }); recyclerView = findViewById(R.id.recycler_view); recyclerView.setAdapter(adapter); } }

Slide 88

Slide 88 text

Converted MainActivity.kt

Slide 89

Slide 89 text

MainActivity.kt class MainActivity : AppCompatActivity() { private lateinit var recyclerView: RecyclerView private var adapter = ItemAdapter() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val toolbar = findViewById(R.id.toolbar) setSupportActionBar(toolbar) findViewById(R.id.fab).setOnClickListener { view -> Snackbar.make(view, "tapped!", Snackbar.LENGTH_LONG) .setAction("Action", null).show() } recyclerView = findViewById(R.id.recycler_view) recyclerView.adapter = adapter } }

Slide 90

Slide 90 text

Convert to Kotlin して終わりではない

Slide 91

Slide 91 text

もっとよいKotlinコードを書く • Convert to Kotlinやlintは⽇々進化している • 「こんな⾵に書けたら便利なのに」は⼤体ある • 「Kotlinらしく」だけでなく「読みやすいコード」 「壊れないコード」を⽬指す

Slide 92

Slide 92 text

Kotlin移⾏で気をつけること • Javaを全く意識しなくてよいわけではない • 特にNull許容型

Slide 93

Slide 93 text

Enjoy Kotlin!

Slide 94

Slide 94 text

ref. • Getting started with Android and Kotlin - Kotlin Programming Language : https://kotlinlang.org/ docs/tutorials/kotlin-android.html • Get Started with Kotlin on Android | Android Developers : https://developer.android.com/kotlin/ get-started • [Kotlinイン・アクション | マイナビブックス](https:// book.mynavi.jp/ec/products/detail/id=78137)