Kotlin文法 - this、等価性、演算子オーバーロード、Null安全、例外


# はじめに

[Kotlin文法 - 分解宣言、範囲、型チェックとキャスト](http://qiita.com/k5n/items/c8bf7a507b64f20eebd0)の続き。

[Kotlin Reference](https://kotlinlang.org/dscs/reference/)のOther章This expressions, Equality, Operator overloading, Null Safety, Exceptionsの大雑把日本語訳。適宜説明を変えたり端折ったり補足したりしている。


# this

現在のレシーバを表すのに *this* を使う。

 - クラスのメンバの中で、*this* はそのクラスの現在のオブジェクトを指す。
 - 拡張関数やレシーバ付き関数リテラルの中で、*this* はドットの左側として渡されるレシーバパラメータを表す。

*this* が他のスコープにあるものを指すならラベルを付ける必要がある。ラベルが付いていない素の *this* はスコープの最も内側にあるものを指す。

## 修飾されたthis

```kotlin
class A { // 暗黙的に@Aラベル
  inner class B { // 暗黙的に@Bラベル
    fun Int.foo() { // 暗黙的に@fooラベル
      val a = this@A // Aのthis
      val b = this@B // Bのthis

      val c = this // foo()のレシーバ(Intオブジェクト)
      val c1 = this@foo // foo()のレシーバ(Intオブジェクト)

      val funLit = @lambda {String.() ->
        val d = this // funLitのレシーバ
        val d1 = this@lambda // funLitのレシーバ
      }

      val funLit2 = { (s: String) ->
        val d1 = this  // foo()のレシーバ。ラムダ式の中にはレシーバがないから。
      }
    }
  }
}
```

# 等価性

Kotlinには２種類の等価性がある。

 - 参照の等価性（２つの参照が同じオブジェクトを指しているかどうか）
 - 構造の等価性（*equals()* によるチェック）

## 参照の等価性
 
参照の等価性は *===* 演算子（否定は *!==*）によってチェックできる。*a === b* は *a* と *b* が同じオブジェクトを指している時だけ *true* になる。
 
## 構造の等価性

構造の等価性は *==* 演算子（否定は *!=*）によってチェックできる。*a == b* は以下のように翻訳される。

```kotlin
a?.equals(b) ?: (b === null)
```

つまり *a* が null でなければ *equals(Any?)*  関数が呼ばれ、そうでなければ（つまり *a* が *null*）、*b* が *null* かどうかがチェックされる。

*null* と比較する時に明示的に最適化しても意味がないことに注意。*a == null* は自動的に *a === null* に変換される。

# 演算子オーバーロード

Kotlinは型に対して事前定義された演算子のセットを提供することができる。これらの演算子は固定の記号（+ とか \* のような）による表現と、固定の優先順位を持つ。演算子を実装するには対応する型に対して決まった名前のメンバ関数か拡張関数を用意する。対応する型というのはつまり二項演算子の左側の型と単項演算子の引数の型。演算子をオーバーロードする関数には *operator* 修飾子を付ける必要がある。

※なんか分かりにくい説明なので補足。つまり演算子は決まった名前のメソッド呼び出しに変換される仕組み。使える記号とその優先順位、呼び出されるメソッドが、あらかじめ決まってる。

## 変換ルール

### 単項演算子

| 式 | 変換先 |
|:-- |:-- |
| +a | a.unaryPlus() |
| -a | a.unaryMinus() |
| !a | a.not() |

この表が示しているのは、コンパイラが処理する時に、例えば *+a* という表現に対して次のステップを実行する。

 - *a* の型を決める。これを *T* としよう。
 - レシーバ *T* に対して *operator* 修飾子のついた引数なしの *unaryPlus()* 関数を探す。つまりメンバ関数か拡張関数から。
 - もし関数が見つからないか曖昧ならコンパイルエラー
 - もし関数が見つかってその戻り値の型が *R* なら、式 *+a* の型は *R*

これらの演算子は基本型に対しては最適化され、関数呼び出しのオーバーヘッドはかからないことに注意。

| 式 | 変換先 |
|:-- |:-- |
| a++ | a.inc() + 以下参照 |
| a-- | a.dec() + 以下参照 |

これらの演算子はレシーバ自身を変更して（オプションとして）値を返すことを想定している。

注！！ __inc()/dec() はレシーバオブジェクト（の状態）を変更すべきではない。__「レシーバ自身を変更」とは *レシーバ変数* を意味していて、レシーバオブジェクトのことではない。

コンパイラは後置型（つまり *a++*）の演算子の解析を以下のステップで行う。

 - *a* の型を決める。これを *T* としよう。
 - レシーバ *T* に対して *operator* 修飾子のついた引数なしの *inc()* 関数を探す。
 - もし戻り値の型が *R* なら、それは *T* のサブ型でなければならない。

式の計算は以下のように行われる。

 - *a* の初期値を一時的に *a0* に格納
 - *a.inc()* の結果を *a* に代入
 - *a0* を式の結果として返す

*a--* でもステップは同じ（*inc* が *dec* になるだけ）。

前置の *--a* や *++a* の場合も同じように解析され、式の計算は以下のようになる。

 - *a.inc()* の結果を *a* に代入
 - 式の結果として新しい *a* の値を返す

### 二項演算子

| 式 | 変換先 |
|:-- |:-- |
| a + b | a.plus(b) |
| a - b | a.minus(b) |
| a * b | a.times(b) |
| a / b | a.div(b) |
| a % b | a.mod(b) |
| a..b | a.rangeTo(b) |

上の表の演算子はコンパイラが変換先の式に置き換える。

| 式 | 変換先 |
|:-- |:-- |
| a in b | b.contains(a)
| a !in b | !b.contains(a)

*in*, *!in* も同じなんだけど、レシーバと引数の順番が反対。

| 式 | 変換先 |
|:-- |:-- |
| a[i] | a.get(i)
| a[i, j] | a.get(i, j)
| a[i_1, ..., i_n] | a.get(i_1, ..., i_n)
| a[i] = b | a.set(i, b)
| a[i, j] = b | a.set(i, j, b)
| a[i_1, ..., i_n] = b | a.set(i_1, ..., i_n, b)

[]括弧は適切な数の引数の *get()* や *set()* に置き換えられる。

| 式 | 変換先 |
|:-- |:-- |
| a() | a.invoke() |
| a(i) | a.invoke(i) |
| a(i, j) | a.invoke(i, j) |
| a(i_1, ..., i_n)| a.invoke(i_1, ..., i_n) |

()括弧は適切な引数の *invoke()* に置き換えられる[^1]。

[^1]: C++のファンクタと同じものが作れる。実はラムダは中身がinvoke()メソッドで実行されるオブジェクトを生成してる。

| 式 | 変換先 |
|:-- |:-- |
| a += b | a.plusAssign(b) | 
| a -= b | a.minusAssign(b ) |
| a *= b | a.timesAssign(b ) |
| a /= b | a.divAssign(b) | 
| a %= b | a.modAssign(b) | 

代入演算子（*a += b*）に対してはコンパイラは以下のステップを実行する。

 - もし変換先の関数が存在するなら
     - もし対応する二項演算子（*plusAssign()* に対してなら *plus()*）が存在するなら、エラーを報告する（曖昧なので）。
     - 戻り値の型が *Unit* かどうか確認し、そうでなければエラーを報告。
     - *a.plusAssign(b)* のコードを生成
 - そうでなければ *a = a + b * を生成しようとする。（これには型チェックを含む。つまり *a + b* は *a* のサブ型でなければならない。）

注：Kotlinでは代入は式ではない。

| 式 | 変換先 |
|:-- |:-- |
| a == b | a?.equals(b) ?: b === null |
| a != b | !(a?.equals(b) ?: b === null) |

注：*===* と *!==* はオーバーロードできない。なのでそれらの変換ルールはない。

*==* 演算子は特別。*null* かどうかを調べるために複雑な式に変換される。そして *null == null* は *true* である。

| 式 | 変換先 |
|:-- |:-- |
| a > b | a.compareTo(b) > 0 |
| a < b | a.compareTo(b) < 0 |
| a >= b | a.compareTo(b) >= 0 |
| a <= b | a.compareTo(b) <= 0 |

全ての比較は *compareTo()* に変換される。戻り値には *Int*  が要求される。

## 関数の接中辞呼び出し

[関数の接中辞呼び出し](http://qiita.com/k5n/items/964d765767a65cc3de5b#%E6%8E%A5%E4%B8%AD%E8%BE%9Einfix%E8%A8%98%E6%B3%95)によって、独自の接中辞演算子のように振る舞わせることができる。

# Null安全

## Nullable型とNullにならない型

Kotlinの型システムはnull参照[^2]を排除することを狙っている。

[^2]: 「[１０億ドルの過ち](https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%B3%E3%83%88%E3%83%8B%E3%83%BC%E3%83%BB%E3%83%9B%E3%83%BC%E3%82%A2#.E8.AA.9E.E9.8C.B2)として知られている」という記述が原文にある。

Javaを含む多くのプログラミング言語の共通の落とし穴の１つは、null参照例外を引き起こすnull参照のメンバへのアクセスだ。Javaでは *NullPointerException* または略して *NPE* を投げる。

KotlinでNPEが起こり得るのは、

 - 明示的に *throw NullPointerException()* を呼んだとき
 - 外部のJavaコードが起こしたとき
 - 初期化に関係する何らかの不整合があったとき（コンストラクタ内では有効な未初期化のthisがどこかで使われた[^3]）
 
[^3]: 別スレッドで初期化途中のオブジェクトにアクセスする場合のことっぽい。

Kotlinでは、型システムは *null* を持ちうる参照とそうでない参照を区別する。例えば *String* 型の通常の変数は *null* を入れられない。

```kotlin
var a: String = "abc"
a = null // コンパイルエラー
```

*null* を許可するには nullable な *String* （*String?* と書く）として変数を宣言する。

```kotlin
var b: String? = "abc"
b = null // ok
```

*a* のプロパティにアクセスしたりメソッドを呼んだりしても、NPEを起こさないことが保証されている。なので安全に次のように書ける。

```kotlin
val l = a.length
```

けど *b* の同じプロパティにアクセスしたくても、安全ではないので、コンパイラはエラーを報告する。

```kotlin
val l = b.length // error: 変数 'b' は null がありうる
```

んなこと言ったってプロパティにアクセスする必要があるよね？それには幾つかの方法がある。

## nullかどうかチェックする

まず明示的に *null* かどうかチェックして、そうだった場合とそうでない場合を別々に扱うことができる。

```kotlin
val l = if (b != null) b.length else -1
```

コンパイラはチェックを行ったことを追跡していて、*if* の中で *length* を呼ぶのを許可する。もっと複雑な条件もサポートする。

```kotlin
if (b != null && b.length > 0)
  print("String of length ${b.length}")
else
  print("Empty string")
```

これは *b* が変更されない場合にだけ動作することに注意（つまりチェックと利用の間でローカル変数が変更されないか、オーバーライドできない *val* プロパティか）。そうでないとチェックの後で *null* に変わっちゃうかもしれないから。

## 安全な呼び出し

２つ目のやり方は安全な呼び出し演算子を使う方法。*?* と書く。

```kotlin
b?.length
```

これは *b* が *null* でなければ *b.length* を返し、そうでなければ *null* を返す。この式の型は *Int?* である。

これはチェイン呼び出しを行うのに便利。例えばBobという従業員が部署に配属されている（またはされていない）かもしれないとする。その部署には他の従業員がボスとしている（またはいない）かもしれないとする。もしありえるなら、Bobの部署のボスの名前を取得したいとすると、

```kotlin
bob?.department?.head?.name
```

これは途中のどれかが *null* なら *null* を返す。

## エルビス演算子

nullableな参照 *r* があって、「*r* が *null* でなければそれを使うけど、そうじゃないなら別のnullじゃない *x* を使うよ」って場合は *if* を使うとこう書くことになる。

```kotlin
val l: Int = if (b != null) b.length else -1
```

この*if* 式はエルビス演算子[^4] *?:* を使って表現できる。

[^4]: Goovy由来の機能。名前はElvis Presleyの髪型から。"If you look at it sideways, you'll recognize.（横から見りゃ誰だかわかる）"

```kotlin
val l = b?.length ?: -1
```

エルビス演算子の左側が *null* でなければそれを返し、そうでなければ右側を返す。右側は左側が *null* の場合しか評価されないことに注意。

Kotlinでは *return* や *throw* も式[^5]なので、これらはエルビス演算子の右側に使える。これは例えば関数の引数チェックでとても便利だ。

[^5]: じゃあこれらが何の値を返すのか？動作からして何も(Unitさえ）返さないはずなので、式ではなく文だと思うのだが・・・実際に式として利用できる。val a:Int = throw IllegalArgumentException("dummy") とか書いても（val a:Int 部分に到達しないという警告は出るが）コンパイルを通る。このときaの型は何でもいい。式として使えるが、それが何も返さないことをコンパイラが把握できるので「吾輩は式である。戻り値の型はない。」ってことみたい。なので戻り値を利用する if や when の中でも使える。

```kotlin
fun foo(node: Node): String? {
  val parent = node.getParent() ?: return null
  val name = node.getName() ?: throw IllegalArgumentException("name expected")
  // ...
}
```

## !!演算子

３つ目の選択肢はNPE大好きっ子のためのもの。*b!!* って書くと nullでない *b* の値（つまりここの例では *String*）を返し、*b* が *null* ならNPEを投げる。

```kotlin
val l = b!!.length()
```

もしNPEが欲しいならこれを使えばいい。けど明らかに墓穴を掘ることになり、暗闇を抜けて青空を仰ぎ見ることはできない。

# 例外

## 例外クラス

Kotlinでは全ての例外クラスは *Throwable* の子孫。全ての例外はメッセージとスタックトレースと、オプションで原因を持つ。

例外オブジェクトを投げるには、*throw* 式を使う。

```kotlin
throw MyException("Hi There!")
```

例外をキャッチするには *try* 式を使う。

```kotlin
try {
  // なんかのコード
}
catch (e: SomeException) {
  // 例外処理
}
finally {
  // オプションでfinallyブロック。例外が起こっても起こらなくても実行される。
}
```

ゼロ個以上の数の *catch* ブロックが持てる。*finally* は省略されるかもしれない。けど少なくとも１つの *catch* か *finally* がなければならない。

### tryは式である

tryは式であり、値を返すかもしれない。

```kotlin
val a: Int? = try { parseInt(input) } catch (e: NumberFormatException) { null }
```

*try* の戻り値は *try* ブロックの最後の式か、*catch* ブロックの最後の式になる。*finally* ブロックの内容は式の結果に影響しない。

## 検査例外

Kotlinには検査例外(checked exception)はない。これには多くの理由があるのだけど、簡単な例を挙げよう。

以下は *StringBuilder* クラスによって実装されているJDKのインターフェースの例。

```kotlin
Appendable append(CharSequence csq) throws IOException;
```

これが何を言っているのか？何か（*StringBuilder* やある種のログ、コンソールなど）に文字列を追加するたび、*IOException* をキャッチしないといけないと言っている。なぜか？これはIOを実行するかもしれないから（*Writer* も *Appendable* を実装している）・・・。それでこういうコードをそこら中にばら撒くことになる。

```kotlin
try {
  log.append(message)
}
catch (IOException e) {
  // Must be safe
}
```

これはイケてない。（Effective Javaの６５項「例外を無視するな」を参照）

Bruce Eckelは [Javaに検査例外っていんの？](http://www.mindview.net/Etc/Discussions/CheckedExceptions) の中で言っている。

> 小さなプログラムの検査では次の結論を導く。要求される例外の仕様は開発者の生産性とコードの質の両方を向上させうる。しかし巨大なソフトウェアプロジェクトの経験は異なる結果を示唆している - 生産性を低下させ、ほとんどまたは全くコードの質を向上させない。

他の批評はこんなの。

 - [Javaの検査例外は誤ちだった](http://radio-weblogs.com/0122027/stories/2003/04/01/JavasCheckedExceptionsWereAMistake.html) (Rod Waldhoff)
 - [検査例外のトラブル](http://www.artima.com/intv/handcuffs.html) (Anders Hejlsberg)

## Javaとの相互運用性

Javaとの相互運用性については[Javaとの相互運用](http://qiita.com/k5n/items/ceb1f921b27d62fc9fcb)の検査例外を参照

# 次の章へ

次は[Kotlin文法 - アノテーション、リフレクション、型安全なビルダー、動的型](http://qiita.com/k5n/items/3eaafc5aa5a07cd02f5c)へGO!