Kotlin のクラス

Kotlin を勉強中なので、自分用まとめ

## クラス

クラスの定義とインスタンス化

```kotlin
class MyClass {
    private var str = "aaa"
    var num = 1
    
    fun showData () {
        println("Str = ${str}, Num = ${num}")
    }
}

val sample = MyClass()
sample.showData()
```

アクセス修飾子

| 修飾子    | 説明                                     |
| --------- | ---------------------------------------- |
| public    | 全てのクラスからアクセス可能             |
| protected | クラスとサブクラス内からアクセス可能     |
| internal  | 同じモジュール内のクラスからアクセス可能 |
| private   | クラス内からのみアクセス可能             |



## プロパティ

Kotlin ではフィールド（クラス内変数）が使えないが、代わりにプロパティが使える

* プロパティ
  * アクセサー（getter/setter）を持つ
  * getter / setter の記述は省略可能
  * getter / setter を省略した場合、単純な値の get / set が提供される



### プロパティの定義

```
var プロパティ名: 型 = 初期値
  getter 関数
  setter 関数
```

```kotlin
class MyClass {
    var num = 1
      set(value) {  // 引数は value が使われる
          if (value < 0) {
              println("Illegal Value")
          } else {
              field = value
          }
      }
}

fun main() {
    val sample = MyClass()
    
    sample.num = -2     //=> Illegal Value
    sample.num = 5
    println(sample.num) //=> 5
}
```



## コンストラクタ

### プライマリコンストラクタ

クラスに一つだけ記述されるコンストラクタ

```kotlin
class MyClass constructor(str: String, num: Int) {
    var str: String
    var num: Int
    
    init {
        this.str = str  // プロパティと引数が同じ名前のときは this を付けて差別化する
        this.num = num
    }

    fun showData () {
        println("Str = ${str}, Num = ${num}")
    }
}

val sample = MyClass("abc", 3)
sample.showData()  //=> Str = abc, Num = 3
```

プライマリコンストラクタのキーワードは省略可能

```kotlin
class MyClass (str: String, num: Int) {}
```

`var` / `val` を付けると、宣言と初期化を同時に行える

```kotlin
class MyClass (var str: String, var num: Int) {
    fun showData () {
        println("Str = ${str}, Num = ${num}")
    }
}
```

### セカンダリコンストラクタ

ひとつのクラスに2つ以上のコンストラクタを定義

初期化の処理を分岐させるような場合に使用される

```kotlin
class MyClass constructor(var str: String, var num: Int) {
    constructor(str: String):this(str, 1) {}
    constructor():this("xyz") {}
    fun showData () {
        println("Str = ${str}, Num = ${num}")
    }
}

val sample1 = MyClass("abc", 3)
val sample2 = MyClass("aaa")
val sample3 = MyClass()

sample1.showData() //=> Str = abc, Num = 3
sample2.showData() //=> Str = aaa, Num = 1
sample3.showData() //=> Str = xyz, Num = 1
```



## 継承

継承元のクラス、メソッドには、`open` 修飾子が必要

```kotlin
open class BaseClass(var a: Int) {
    open fun showData(){
        println(this.a)
    }
}

class MyClass(a: Int, var b: String) :BaseClass(a)

val instance = MyClass(1, "abc")
instance.showData()  //=> 1
```



### オーバーライド

`override` 修飾子をつけることで、継承元のメソッドを上書きする

```kotlin
open class BaseClass(var a: Int) {
    open fun showData(){
        println(this.a)
    }
}

class MyClass(a: Int, var b: String) :BaseClass(a) {
    override fun showData(){
        println("${this.b}")
        super.showData()  // 継承元のメソッドを呼び出す
    }
}

val instance = MyClass(1, "abc")
instance.showData()  //=> abc, 1
```



## 抽象クラス

`abstract` 修飾子を付けることで、抽象クラス・メソッドになる

抽象メソッドは、実装部分を持つことはできない

```kotlin
abstract class AbstClass(var a: Int) {
    abstract fun showData()
}

class MyClass(a: Int, var b: String) :AbstClass(a) {
    override fun showData(){
        println("${this.b}, ${this.a}")
    }
}

val instance = MyClass(1, "abc")
instance.showData()
```



## データクラス

データを保持するだけで、処理を持たないクラス

```kotlin
data class MyClass(val a: Int, val b: String)
```

データクラスの条件

* 一つ以上の引数を持つ
* 引数にはすべて  `var`  か `val` を付与
* `abstract` `open` `sealed` `inner` 修飾子は付けられない

データクラスにはいくつかのメソッドが用意されている

#### equals メソッド

同値性を確認する

```kotlin
data class MyClass(var a: String = ""){
    var b: Int = 0
}

val m1 = MyClass("abc")
m1.b = 1
val m2 = MyClass("abc")
m2.b = 2

println(m1 == m2)  //=> true  （== で equals メソッドが呼ばれる）
```

#### toString メソッド

データクラスのオブジェクトを文字列化

println 関数を実行時に暗黙で呼ばれる

#### componentN

プロパティの値を個々の変数に分解する

```kotlin
val t = Triple("a", "b", 10)

println(t.component1())  //=> a
println(t.component2())  //=> b
println(t.component3())  //=> 10
```

分割代入で使用されている

```kotlin
val t = Triple("a", "b", 10)
val (x, y, z) = t
println(x)  //=> a
println(y)  //=> b
println(z)  //=> 10
```

#### copy

オブジェクトのコピー

```kotlin
data class MyClass(val x: String, val y: String, val z: Int)

val m1 = MyClass("a", "b", 1)
val m2 = m1.copy(x = "c")

println(m2)  //=> MyClass(x=c, y=b, z=1)
```



## オブジェクト式

再利用しないクラス

```
object {クラス本体}
```

```
object:  基底クラス or インターフェイス {クラス本体}
```

Android 開発において、ボタンクリック時の処理を定義するときなどに使用できる

btn.setOnClickListener の引数はクラスである必要があるが、通常は他のボタンでは再利用しないので、オブジェクト式が使える

```kotlin
btn.setOnClickListener(object: View.OnClickListener {
  override fun onClick(view: View) {
    // ボタンクリック時の処理
  }
})
```

SAM（Single Abstract Method）インターフェース

* 抽象メソッドをひとつだけ持つインターフェース
* ラムダ式で置き換えられる

```kotlin
// 上プログラムをラムダ式で置き換えたもの
btn.setOnClickListener{ クリックされた時の処理 }
```



## オブジェクト宣言

ひとつのインスタンスしか持たないクラス

```
object オブジェクト名 {オブジェクト本体}
```

```kotlin
object MyObject {
    val x = "a"
    var y = 10

    fun showData(){
        println("${x}, ${y}")
    }
}
    
MyObject.y = 20
MyObject.showData()  //=> a, 20
```



##  ジェネリック型

データ型に依存しない、汎用的なコード記述

```kotlin
class MyClass<T>(var value:T) {
    fun getVal(): T {
        return value
    }
}

val m1 = MyClass<String>("aaa")
val m2 = MyClass<Int>(10)
println(m1.getVal())  //=> aaa
println(m2.getVal())  //=> 10
m1.value = 1  // Error
```

###  ジェネリック関数

関数の呼び出し時に引数と戻り値の型を決める

```kotlin
fun <T> myFunction(arr: Array<T>): T = arr[arr.size - 1]

val a1 = arrayOf(1, 2, 3)
val a2 = arrayOf("a", "b", "c")

println(myFunction<Int>(a1))
println(myFunction<String>(a2))
```



## ネストクラス

クラスの中にクラスを定義することができる

クラスA内で定義されたクラスBは、クラスA 内部からしか呼ぶことができない

```kotlin
class MyClassA{
    private class MyClassB{
        fun show () = println("class B")
    }
    
    fun run() {
        val instB = MyClassB()
        instB.show()
    }
}

val instA = MyClassA()
instA.run()  //=> class B
```