古典的な for 文
// Java
for (int i = 0; i <= 9; i++) {
System.out.println(i);
}
は Kotlin では次のように書けます。
(0..9).forEach {
println(it)
}
しかし、..
ってなんでしょうか。
0..9
の型はなんでしょうか。
..
演算子オーバーロード
Kotlin では演算子オーバーロードによって、演算子の処理を 被演算子の型ごとに 関数(メンバ関数もしくは拡張関数)として実装しています。1
関数名は演算子によって決まっています。(..
の場合は rangeTo
です。)
被演算子(関数の引数。拡張関数の場合はレシーバーも含む)と演算結果(関数の返値)の型は任意(演算子の種類によって制限されることはない)のようです。
rangeTo
rangeTo
関数の実装は標準ライブラリーには次のものがあります。
引数 | 返値 | APIドキュメント |
---|---|---|
Comparable<T> |
ClosedRange<Comparable<T>> |
kotlin.ranges |
Double |
ClosedFloatingPointRange<Double> |
kotlin.ranges |
Float |
ClosedFloatingPointRange<Float> |
kotlin.ranges |
一方が Long 、他方が Long / Int / Short / Byte
|
LongRange |
Long / Int / Short / Byte |
Int / Short / Byte
|
IntRange |
Int / Short / Byte |
Char |
CharRange |
Char |
ULong |
ULongRange |
ULong |
UInt |
UIntRange |
UInt |
UShort |
UIntRange |
UShort |
ここに現れる型をクラス図に表すと次のようになります。
引数:
PlantUML
```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 ```返値:
PlantUML
```uml @startuml ClosedRange <|-- ClosedFloatingPointRange ClosedRange <|.. LongRange ClosedRange <|.. IntRange ClosedRange <|.. CharRange ClosedRange <|.. ULongRange ClosedRange <|.. UIntRange interface ClosedRange interface ClosedFloatingPointRange @enduml ```つまり標準ライブラリーが用意している rangeTo
は2つの Comparable
から ClosedRange
を生成する関数ということになります。
ClosedRange
ClosedRange
インターフェイスは閉じた範囲を表します。
プロパティとして範囲の始点と終点のみを持ち、
独自のメンバ関数(拡張関数でない)としては指定された値が範囲に含まれるかどうかと範囲が空かどうかのみを持ちます。
実は Comparable
インターフェイスを実装しているため、上記に加えて同型オブジェクトとの大小比較を行うメンバ関数も持っていますが、
これ以外のプロパティ・メンバ関数は持っていません。
おや、これでは forEach
拡張関数で反復処理するための情報が足りませんね。
ということはこのインターフェイスを継承・実装している型に反復処理のためのプロパティやメンバ関数があるということになります。
実際、ClosedRange
や ClosedFloatingPointRange
に対しては forEach
は使えません。
(0.0..9.0) // ClosedFloatingPointRange を返す。
.forEach { // コンパイルエラー!
println(it)
}
Iterable
rangeTo
関数の返値の型を先ほどクラス図にしましたが、そのときは未出の型を省いていました。
ここで完全版を見てみましょう。
PlantUML
```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 ```Iterable
が出てきましたね!
Iterable
には forEach
の拡張関数があるので、Iterable
を実装している IntRange
などの型には forEach
を使えるのです。
まとめ
-
..
は2項演算子で、rangeTo
関数に変換される。 -
0..9
は0.rangeTo(9)
に変換され、IntRange
型オブジェクトを返す。 -
..
演算の被演算子の型によってはforEach
関数を使えない。
参考
Ranges - Kotlin Programming Language
Operator overloading - Kotlin Programming Language
-
+
演算子による整数同士の加算などでもそうです。→plus - Kotlin Programming Language ↩