Kotlinの紹介（社内プレゼン大会資料）

# 先週のGoole I/O 2017で、KotlinがAndroidの公式言語の1つとなると、発表されました

Javaも残ります

---

# Kotlinとは

* JVM言語（Javaと相互運用できる）
* 型推論
    * ```var str = "abc"```
* null安全
    * ```var strNullable: String?```
    * ```var strNotNull: String```
* 関数型プログラミング
    * ```listOf(1, 2, 3).map { it * 2 }.filter { it > 2 }.forEach { print(it) }```

---
# 実際のコードで紹介

* 画像とテキストのViewをグリッド表示する
* 各Viewは、スクロールin/outで再利用される

![ss_png.png](https://qiita-image-store.s3.amazonaws.com/0/107584/d5c5022b-24b2-7414-e64e-39a01dc25710.png)

各Viewを作るコードで見てみます

---

```java
static class ViewHolder {
    TextView textView;
    ImageView imageView;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    // Viewを準備する
    ViewHolder holder;
    if (convertView == null) {
        // convertViewがなかったら、xmlからViewを作る
        convertView = LayoutInflater.from(mContext).inflate(R.layout.dialog_grid_menu_item, null);

        // パフォーマンスのため、各sub viewをHolderに保持し、Holder自体もViewに保持しておく
        holder = new ViewHolder();
        holder.imageView = (ImageView) convertView.findViewById(R.id.icon); // ちょっと遅い
        holder.textView = (TextView) convertView.findViewById(R.id.text); // ちょっと遅い
        convertView.setTag(holder);
    } else {
        // convertViewがあれば、再利用する。
        holder = (ViewHolder) convertView.getTag();
    }

    // データをViewに反映する
    Data data = getItem(position);
    holder.imageView.setImageResource(data.imageId);
    holder.textView.setText(data.textId);

    return convertView;
}
```

---

```kotlin
internal class ViewHolder {
    var textView: TextView? = null
    var imageView: ImageView? = null
}

override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
    var convertView = convertView
    // Viewを準備する
    val holder: ViewHolder
    if (convertView == null) {
        // convertViewがなかったら、xmlからViewを作る
        convertView = LayoutInflater.from(mContext).inflate(R.layout.dialog_grid_menu_item, null)

        // パフォーマンスのため、各sub viewをHolderに保持し、Holder自体もViewに保持しておく
        holder = ViewHolder()
        holder.imageView = convertView.findViewById(R.id.icon) as ImageView // ちょっと遅い
        holder.textView = convertView!!.findViewById(R.id.text) as TextView // ちょっと遅い
        convertView.tag = holder
    } else {
        // convertViewがあれば、再利用する。
        holder = convertView.tag as ViewHolder
    }

    // データをViewに反映する
    val data = getItem(position)
    holder.imageView!!.setImageResource(data.imageId)
    holder.textView!!.setText(data.textId)

    return convertView
}
```

---

data classで、fieldとconstructorを同時に定義

```diff
- internal class ViewHolder {
-     var imageView: ImageView? = null
-     var textView: TextView? = null
- }
+ data class ViewHolder(val textView: TextView, val imageView: ImageView)
```

constructorを使えば1行

```diff
- holder = ViewHolder()
- holder.imageView = convertView.findViewById(R.id.icon) as ImageView // ちょっと遅い
- holder.textView = convertView!!.findViewById(R.id.text) as TextView // ちょっと遅い
+ holder = ViewHolder(convertView.findViewById(R.id.text) as TextView, convertView.findViewById(R.id.icon) as ImageView)
```

Kotlin Android ExtensionによるfindViewByIdのwrapperを使う

```diff
- holder = ViewHolder(convertView.findViewById(R.id.text) as TextView, convertView.findViewById(R.id.icon) as ImageView)
+ holder = ViewHolder(convertView.text, convertView.icon)
```

---

いちいち、一次変数を使いたくない

also

```diff
// convertViewがなかったら、xmlからViewを作る
- view = LayoutInflater.from(mContext).inflate(R.layout.dialog_grid_menu_item, null)
- view.tag = ViewHolder(it.text, it.icon)
+ view = LayoutInflater.from(mContext).inflate(R.layout.dialog_grid_menu_item, null).also {
+     it.tag = ViewHolder(it.text, it.icon)
+ }
```


run

```diff
// データをViewに反映する
- val data = getItem(position)
- holder.imageView!!.setImageResource(data.imageId)
- holder.textView!!.setText(data.textId)
+ getItem(position).run {
+     holder.imageView.setImageResource(imageId)
+     holder.textView.setText(textId)
+ }
```

---

ifは式なので、直接代入する

```diff
- var view: View
- if (convertView == null) {
    // convertViewがなかったら、xmlからViewを作る
-     view = LayoutInflater.from(mContext).inflate(R.layout.dialog_grid_menu_item, null).also {
+ var view: View = if (convertView == null) {
+     LayoutInflater.from(mContext).inflate(R.layout.dialog_grid_menu_item, null).also { 
          // パフォーマンスのため、各sub viewをHolderに保持し、Holder自体もViewに保持しておく
          it.tag = ViewHolder(it.text, it.icon)
      }
  } else {
      // convertViewがあれば、再利用する。
-     view = convertView
+     convertView
  }
```

---

?: 演算子で、if-nullをなくす

```diff
- var view: View = if (convertView == null) {
-     // convertViewがなかったら、xmlからViewを作る
-     LayoutInflater.from(mContext).inflate(R.layout.dialog_grid_menu_item, null).also {
+ var view: View = convertView ?: LayoutInflater.from(mContext).inflate(R.layout.dialog_grid_menu_item, null).also {
          // パフォーマンスのため、各sub viewをHolderに保持し、Holder自体もViewに保持しておく
          it.tag = ViewHolder(it.text, it.icon)
      }
- } else {
-     // convertViewがあれば、再利用する。
-     convertView
  }
```

---

# 元のJavaコード

```java
static class ViewHolder {
    TextView textView;
    ImageView imageView;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    // Viewを準備する
    ViewHolder holder;
    if (convertView == null) {
        // convertViewがなかったら、xmlからViewを作る
        convertView = LayoutInflater.from(mContext).inflate(R.layout.dialog_grid_menu_item, null);

        // パフォーマンスのため、各sub viewをHolderに保持し、Holder自体もViewに保持しておく
        holder = new ViewHolder();
        holder.imageView = (ImageView) convertView.findViewById(R.id.icon); // ちょっと遅い
        holder.textView = (TextView) convertView.findViewById(R.id.text); // ちょっと遅い
        convertView.setTag(holder);
    } else {
        // convertViewがあれば、再利用する。
        holder = (ViewHolder) convertView.getTag();
    }

    // データをViewに反映する
    Data data = getItem(position);
    holder.imageView.setImageResource(data.imageId);
    holder.textView.setText(data.textId);

    return convertView;
}
```

---

# 書き換えたKotlinコード

```kotlin
data class ViewHolder(val textView: TextView, val imageView: ImageView)

override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
    // Viewを準備する
    val view: View = convertView ?: LayoutInflater.from(mContext).inflate(R.layout.dialog_grid_menu_item, null).also {
        // パフォーマンスのため、各sub viewをHolderに保持し、Holder自体もViewに保持しておく
        it.tag = ViewHolder(it.text, it.icon)
    }

    // データをViewに反映する
    (view.tag as ViewHolder).let { (textView, imageView) ->
        getItem(position).run {
            textView.setText(textId)
            imageView.setImageResource(imageId)
        }
    }

    return view
}
```

---

thanks