Kotlin文法 - インターフェース、アクセス修飾子、拡張


# はじめに

[Kotlin文法 - クラス、継承、プロパティ](http://qiita.com/k5n/items/35e76d79ee9de4effb89)の続き。

[Kotlin Reference](https://kotlinlang.org/docs/reference/)のClasses and Objects章Interfaces, Inheritance, Visibility Modifiers, Extensionsの大雑把日本語訳。適宜説明を変えたり端折ったり補足したりしている。

# インターフェース

Kotlinの *interface* はJava8のそれにとてもよく似ている。抽象メソッドだけでなく実装も持てる。ただし抽象クラスと違って状態を持てない。

```kotlin
interface MyInterface {
    fun bar()
    fun foo() {
      // デフォルト実装を持てる
    }
}

// classとobjectは１つまたは複数のinterfaceを実装できる
class Child : MyInterface {
   override fun bar() {
      // オーバーライドする内容
   }
}
```

## インターフェースのプロパティ

プロパティは持てるが、*abstract* であるかまたはアクセサの実装を提供する必要がある。ようするにバッキングフィールドを持てない。なので初期値を与えたりアクセサ内で *field* にアクセスできない。

```kotlin
interface MyInterface {
    // バッキングフィールドはないので値は与えられない
    val property: Int // abstract

    // getterの実装を与えることはできる
    val propertyWithImplementation: String
        get() = "foo"

    fun foo() {
        // 値はなくてもオーバーライドされる前提でプロパティにアクセスできる
        print(property)
    }
}

class Child : MyInterface {
    // abstractになってるpropetryをオーバーライド
    override val property: Int = 29
}
```

## オーバーライド競合の解決

これ、クラスのとこでも説明したよね？

```kotlin
interface A {
  fun foo() { print("A") }
  fun bar()
}

interface B {
  fun foo() { print("B") }
  fun bar() { print("bar") }
}

class C : A {
  // Aのbar()をオーバーライド
  override fun bar() { print("bar") }
  
  // foo()の方はAのデフォルト実装を利用
}

class D : A, B {
  // AもBもfoo()の実装を持ってるんで、どっち使うかわからん。
  // なのでオーバーライド必須。
  override fun foo() {
    super<A>.foo()    // Aのfoo()を呼び出す
    super<B>.foo()    // Bのfoo()を呼び出す
  }
  
  // bar()の方はBしか実装がないから、オーバーライドしなけりゃそっち使うよ。
}
```

# アクセス修飾子

*public*, *protected*, *private*, *internal* の４つがある。デフォルトは *public* でJavaとは違う。

## パッケージ

トップレベルでは以下のようになる。*protected* はトップレベルでは使えない。

```kotlin:example.kt
package foo

// privateだとこのファイル内だけでしか見えない
private fun foo() {}

// publicなのでこのプロパティはどこからでも見える
public var bar: Int = 5
    private set    // setterはこのファイル内からしか見えない

// internalは同じモジュール内なら見える
internal val baz = 6
```

## クラスとインターフェース

クラス内では

 - *private* はそのクラス内だけでしか見えない
 - *protected* はそのクラスとサブクラスからしか見えない
 - *internal* は同じモジュール内でそのクラスが見えているなら見える
 - *public* そのクラスが見えているなら見える

*protected* の意味はJavaと違ってC++やC#と一緒。またJavaと違って内部クラスの *private* メンバをその外側のクラスから見ることはできない。

```kotlin
open class Outer {
    private val a = 1
    protected val b = 2
    internal val c = 3
    val d = 4  // 何も指定しなければpublic
    
    protected class Nested {
        public val e: Int = 5
    }
}

class Subclass : Outer() {
    // a は見えない
    // b, c, d は見える
    // Nested と e は見える
}

class Unrelated(o: Outer) {
    // o.a, o.b は見えない
    // o.c は見える。 o.d も同じモジュールなので見える
    // Outer.Nested は見えない。なので Nested::e も見えない。
}
```

### コンストラクタ

コンストラクタも何も書かなければデフォルトで *public* なので、前にも書いたけどアクセス制限したいならこう書く。

```kotlin
class C private constructor(a: Int) { ... }
```

## モジュール

さっきから *internal* は同じモジュール内ならって言っているけど、モジュールってのはこういう意味ね[^1]。

 - IntelliJ IDEAモジュール
 - MavenまたはGradleプロジェクト
 - Antタスクの１つの発動でコンパイルされるファイルのセット

[^1]: ええと、パッケージってわけじゃないのね・・・。１コンパイル単位ってこと？よくわからん。SwiftのDynamic Framework内ってのに似てる？ 
 
# 拡張

継承したりデコレータパターンのような仕組みを使わずに、クラスを拡張することができる[^2]。

[^2]: C#やGosuのようにって原文にある。これできる言語は他にも色々。Swiftもできるね。

## 拡張関数

```kotlin
// MutableList<Int>にswapを付け足す
fun MutableList<Int>.swap(index1: Int, index2: Int) {
  val tmp = this[index1]
  this[index1] = this[index2]
  this[index2] = tmp
}
```

```kotlin
val list = mutableListOf(1, 2, 3)
// こんな感じで使える
list.swap(0, 2) // swapメソッドの中のthisはlistを指す
```

```kotlin
// ジェネリクス関数としても追加できる
fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
  val tmp = this[index1]
  this[index1] = this[index2]
  this[index2] = tmp
}
```

ジェネリクスについてはまた後ほど。

## 拡張は静的に解決される

拡張は実際にクラスを書き換えるわけじゃない。クラスに新しいメソッドが追加されるわけじゃないんだ。拡張は静的に呼び出されるってことを声を大にして言いたい！！

```kotlin
open class C

class D: C() // Cを継承

// 拡張でCにfooメソッドを付け足す
fun C.foo() = "c"
// 拡張でDにfooメソッドを付け足す
fun D.foo() = "d"

// Cクラスのインスタンスを受け取る
fun printFoo(c: C) {
    // 拡張の場合、cはCクラスなのでC.foo()を呼び出すように静的に解決される。
    // オーバーライドされたメソッドのように動的に解決されるわけじゃない。
    println(c.foo())
}

// Cの子供のDを渡す。Dに拡張で足したD.foo()が呼び出される・・・よね？
// 残念でした〜！！C.foo()が呼びだされまーす！！
printFoo(D()) // "c"が表示される
```

もし同じ宣言のメンバがあった場合、__メンバが必ず勝つ__。

```kotlin
class C {
    fun foo() { println("正社員") }
}

// Cクラスのメンバーを派遣で置き替えたいんだけど・・・
// ダメです！！常に正社員が優先です！！
fun C.foo() { println("派遣") }

val c = C()
c.foo()    // "正社員"って表示される。拡張で上書きはできない。
```

## 拡張相手がNullableの場合

Nullableでも拡張できる。こうするとnullに対しても呼び出せる。

```kotlin
// Nullableに拡張関数を追加
fun Any?.toString(): String {
    // thisはnullの可能性がある！！
    if (this == null) return "null"
    // nullチェックした後ならもうnullじゃないことがコンパイラには分かるから、
    // thisは勝手にnullでない参照に置き換わる。
    return toString() // これはthis.toString()と同じだけど、thisは既にAny?でなくAny
}
```

## 拡張プロパティ

プロパティも拡張できる。

```kotlin
// List<T>にlastIndexプロパティを付け足す
val <T> List<T>.lastIndex: Int
  get() = size - 1
```

でも関数と同様に実際にクラスにメンバを挿入するわけじゃないから、バッキングフィールドにはアクセスできない。なので__拡張プロパティの初期化はできない__。明示的にgetter/setterを提供することでしか定義できない。

```kotlin
val Foo.bar = 1 // error: 拡張プロパティの初期化はできない！！
```

## コンパニオンオブジェクトの拡張

いやそもそもコンパニオンオブジェクトってなんやねん？ってのは後で説明する。ここではそいつも拡張できるってことを覚えといて。

```kotlin
class MyClass {
  companion object { }  // "Companion"って呼んでね♥︎
}

// MyClassのコンパニオンオブジェクトにfoo()を付け足す
fun MyClass.Companion.foo() {
  // ...
}

// コンパニオンオブジェクトの他のメンバと同じように呼び出せる。
// 呼び出すときはCompanionって付けなくてもいい。貴方に影ながら付き添います。
MyClass.foo()
```

## 拡張のスコープ

大抵の場合はパッケージのトップレベルで定義するよね？

```kotlin
package foo.bar
 
fun Baz.goo() { ... }
```

他のパッケージから使うには *import* しないとダメよ。

```kotlin
package com.example.usage

import foo.bar.goo // "goo"って名前の全ての拡張をインポートする
                   // または
import foo.bar.*   // "foo.bar"の全てをインポートする
// Baz.gooだけを狙い撃ちでインポートってのはできないっぽい

fun usage(baz: Baz) {
  baz.goo()
)
```

## モチベーション

なんでこんな機能用意したかっていうと、JavaだとなんちゃらUtilsってクラスが一杯あるでしょ？例えば *java.util.colletions* って有名な奴使うと、

```java
// Java
Collections.swap(
    list,
    Collections.binarySearch(list, Collections.max(otherList)),
    Collections.max(list)
    );
```

まぁstaticインポートして、

```java
// Java
swap(list, binarySearch(list, max(otherList)), max(list))
```

でもIDEの強力なサポートを受けられないよね？もしこう書けたらもっと良くない？

```java
// Java（もしこうなら）
list.swap(list.binarySearch(otherList.max()), list.max())
```

でもListの中にありうる全ての実装を入れておくなんてしたくないよね？そんなとき拡張が役に立つよ！

# 次の章へ

次は[Kotlin文法 - データクラス, ジェネリクス](http://qiita.com/k5n/items/18adb5c3503a54e96c22)へGO!