【Kotlin】`..` ってなに？

古典的な for 文

```Java
// Java
for (int i = 0; i <= 9; i++) {
    System.out.println(i);
}
```

は Kotlin では次のように書けます。

```Kotlin
(0..9).forEach {
    println(it)
}
```

しかし、`..` ってなんでしょうか。
`0..9` の型はなんでしょうか。

## `..`

[`..` は `rangeTo` 関数に変換される2項演算子です](https://kotlinlang.org/docs/reference/operator-overloading.html#arithmetic)。

### [演算子オーバーロード](https://kotlinlang.org/docs/reference/operator-overloading.html)

Kotlin では[演算子オーバーロード](https://kotlinlang.org/docs/reference/operator-overloading.html)によって、演算子の処理を 被演算子の型ごとに 関数（メンバ関数もしくは拡張関数）として実装しています。[^1]
関数名は演算子によって決まっています。（`..` の場合は `rangeTo` です。）
被演算子（関数の引数。拡張関数の場合はレシーバーも含む）と演算結果（関数の返値）の型は任意（演算子の種類によって制限されることはない）のようです。

[^1]: `+` 演算子による整数同士の加算などでもそうです。→[plus - Kotlin Programming Language](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/plus.html)

## `rangeTo`

`rangeTo` 関数の実装は標準ライブラリーには次のものがあります。

|引数|返値|APIドキュメント|
|---|---|---|
| `Comparable<T>` | `ClosedRange<Comparable<T>>` | [kotlin.ranges](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/range-to.html) |
| `Double` | `ClosedFloatingPointRange<Double>` | [kotlin.ranges](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/range-to.html) |
| `Float` | `ClosedFloatingPointRange<Float>` | [kotlin.ranges](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/range-to.html) |
| 一方が `Long`、他方が `Long` / `Int` / `Short` / `Byte` | `LongRange` | [Long](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-long/range-to.html) / [Int](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/range-to.html) / [Short](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-short/range-to.html) / [Byte](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-byte/range-to.html) |
| `Int` / `Short` / `Byte` | `IntRange` | [Int](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/range-to.html) / [Short](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-short/range-to.html) / [Byte](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-byte/range-to.html) |
| `Char` | `CharRange` | [Char](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-char/range-to.html) |
| `ULong` | `ULongRange` | [ULong](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-u-long/range-to.html) |
| `UInt` | `UIntRange` | [UInt](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-u-int/range-to.html) |
| `UShort` | `UIntRange` | [UShort](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-u-short/range-to.html) |

ここに現れる型をクラス図に表すと次のようになります。

引数：

![image.png](https://qiita-image-store.s3.amazonaws.com/0/61280/1137f73e-ce29-2684-1a2c-bc3383537ceb.png)

<details><summary>PlantUML</summary>
```uml
@startuml
Comparable <|.. Double
Comparable <|.. Float
Comparable <|.. Long
Comparable <|.. Int
Comparable <|.. Short
Comparable <|.. Byte
Comparable <|.. Char
Comparable <|.. ULong
Comparable <|.. UInt
Comparable <|.. UShort
interface Comparable
@enduml
```
</details>

返値：

![image.png](https://qiita-image-store.s3.amazonaws.com/0/61280/cc9679a8-4c83-3ff3-7200-a4a0a8e24656.png)

<details><summary>PlantUML</summary>
```uml
@startuml
ClosedRange <|-- ClosedFloatingPointRange
ClosedRange <|.. LongRange
ClosedRange <|.. IntRange
ClosedRange <|.. CharRange
ClosedRange <|.. ULongRange
ClosedRange <|.. UIntRange
interface ClosedRange
interface ClosedFloatingPointRange
@enduml
```
</details>

つまり標準ライブラリーが用意している `rangeTo` は2つの [`Comparable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-comparable/index.html) から [`ClosedRange`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/-closed-range/index.html) を生成する関数ということになります。

## [`ClosedRange`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/-closed-range/index.html)

[`ClosedRange`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/-closed-range/index.html) インターフェイスは閉じた範囲を表します。
プロパティとして範囲の[始点](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/-closed-range/start.html)と[終点](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/-closed-range/end-inclusive.html)のみを持ち、
独自のメンバ関数（拡張関数でない）としては[指定された値が範囲に含まれるかどうか](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/-closed-range/contains.html)と[範囲が空かどうか](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/-closed-range/is-empty.html)のみを持ちます。
実は [`Comparable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-comparable/index.html) インターフェイスを実装しているため、上記に加えて[同型オブジェクトとの大小比較を行う](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-comparable/compare-to.html)メンバ関数も持っていますが、
これ以外のプロパティ・メンバ関数は持っていません。

おや、これでは `forEach` 拡張関数で反復処理するための情報が足りませんね。
ということはこのインターフェイスを継承・実装している型に反復処理のためのプロパティやメンバ関数があるということになります。
実際、[`ClosedRange`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/-closed-range/index.html) や [`ClosedFloatingPointRange`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/-closed-floating-point-range/index.html) に対しては `forEach` は使えません。

```Kotlin
(0.0..9.0) // ClosedFloatingPointRange を返す。
    .forEach { // コンパイルエラー！
        println(it)
    }
```

## [`Iterable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-iterable/index.html)

`rangeTo` 関数の返値の型を先ほどクラス図にしましたが、そのときは未出の型を省いていました。
ここで完全版を見てみましょう。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/61280/bbaf71b2-304c-33d6-add9-1baafc7ab5af.png)

<details><summary>PlantUML</summary>
```uml
@startuml
Comparable <|-- ClosedRange
ClosedRange <|-- ClosedFloatingPointRange
ClosedRange <|.. LongRange
ClosedRange <|.. IntRange
ClosedRange <|.. CharRange
ClosedRange <|.. ULongRange
ClosedRange <|.. UIntRange
LongProgression <|-- LongRange
IntProgression <|-- IntRange
CharProgression <|-- CharRange
ULongProgression <|-- ULongRange
UIntProgression <|-- UIntRange
Iterable <|.. LongProgression
Iterable <|.. IntProgression
Iterable <|.. CharProgression
Iterable <|.. ULongProgression
Iterable <|.. UIntProgression
interface Iterable
interface Comparable
interface ClosedRange
interface ClosedFloatingPointRange
@enduml
```
</details>

[`Iterable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-iterable/index.html) が出てきましたね！
[`Iterable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-iterable/index.html) には [`forEach`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/for-each.html) の拡張関数があるので、[`Iterable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-iterable/index.html) を実装している [`IntRange`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/-int-range/index.html) などの型には [`forEach`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/for-each.html) を使えるのです。

## まとめ

- `..` は2項演算子で、`rangeTo` 関数に変換される。
- `0..9` は `0.rangeTo(9)` に変換され、[`IntRange`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/-int-range/index.html) 型オブジェクトを返す。
- `..` 演算の被演算子の型によっては `forEach` 関数を使えない。

## 参考

[Ranges - Kotlin Programming Language](https://kotlinlang.org/docs/reference/ranges.html)
[Operator overloading - Kotlin Programming Language](https://kotlinlang.org/docs/reference/operator-overloading.html#arithmetic)