Kotlin/制御構造
このページは、親コンテンツである「Kotlin」の中から {{:Kotlin/制御構造}} の形式で展開されることを意図して書かれています。
この手法は
- ページ分割すると、[[#inline|inline]] のようなページ内リンクが大量に切れる。
- ページ分割すると、<ref name=foobar /> のような名前のついた参照引用情報が大量に切れる。
- スマートフォンやタブレットではページ遷移は好まれない。
- MediaWikiは、圧縮転送に対応しているので1ページのサイズが大きくなるのはトラフィック的には問題が少なく、ページ分割によりセッションが多くなる弊害が大きい。
- 編集はより小さなサブパート(このページ)で行える。
という技術的背景があります。
- Kotlinのサブページ
制御構造[編集]
分岐[編集]
Kotlinは、if と when の2つの分岐構文を持ち、両方とも値を返す式です。
if[編集]
if式は、条件式に基づき分岐し、分岐先の式を評価します。 if式の値は、分岐先の式の値です(C言語系の三項演算子に相当する働きをします)。 if式の値を右辺値化した場合、else節は必須です。
- 構文
if-expr := if '(' 条件式 ')' 式1 [ else 式2 ]
- if式の例
fun main(args: Array<String>) { val i = 0 if (i == 0) println("zero") else println("non zero") println(if (i == 0) "零" else "非零" ) }
- 実行結果
zero 零
条件式の条件[編集]
- 論理型
- Nullableな型
でなければいけません。
- 条件式に整数を使うと
fun main(args: Array<String>) { val i = 0 if (i) println("non zero") }
- コンパイルエラー
Main.kt:4:9: error: type mismatch: inferred type is Int but Boolean was expected if (i) ^
- Kotlinでは、整数は自動的には論理型に変換されないので
if (i != 0) println("non zero")
- とします。
when[編集]
when式を使って条件分岐をすることができます。 when 式は、when に与えられた式(境界値)に最初にマッチするパターンに結びついた値を返すパターンマッチングです。 式が省略されると、パターンの条件が最初に真になるパターンに結びついた値を返します。
- whenの例
fun main() { val ary = arrayOf(1, 'X', 3.14, "abc", arrayOf(1,2,3), true) for (obj in ary) { when (obj) { is Number, is Boolean -> println(obj) is Char -> println("'$obj'") is String -> println("\"$obj\"") is Array<*> -> println(obj.joinToString(prefix="[", postfix="]")) else -> println(obj::class.simpleName) } } }
- 実行結果
1 'X' 3.14 "abc" [1, 2, 3] true
- when式は、いくつかの論理条件に応じて、複数の異なる制御構造体(ケース)のうちの1つを評価することができるという点で、条件式と類似しています[1]。
- 重要な違いは、when式は複数の異なる条件とそれに対応する制御構造体(control structure bodies; CSB)を含むことができることです。
- when式には、境界値付きと境界値なしの2種類の形式があります。
- 境界値(bound value;whenキーワードの後の括弧で囲まれた式)なしのwhen式は、whenエントリからの条件に基づいて異なるCSBのうちの1つを評価します。
- 各 when エントリは、boolean 条件(または特殊な else 条件)とそれに対応する CSB から構成されます。
- when項目は出現順にチェックされ評価され、条件が真と評価された場合、対応するCSBが評価され、when式の値はCSBの値と同じになり、残りのすべての条件と式は評価されません。
- whenパターン式のパターンの後ろに break は不要です(フォールスルーしませんし、することはできません)。
- もし break を書くと、when式の外のループ式からの脱出になります(フォールスルーしてしまう言語には、できなかったこと)。
whenは式なので以下のように書換えることができます。
- when式の例
fun main() { val ary = arrayOf(1, 'X', 3.14, "abc", arrayOf(1,2,3), true) for (obj in ary) { val s = when (obj) { is Number, is Boolean -> obj is Char -> "'$obj'" is String -> "\"$obj\"" is Array<*> -> obj.joinToString(prefix="[", postfix="]") else -> obj::class.simpleName } println(s) } }
区切子 ->
を使った構文の左辺の条件には、以下のようなバリエーションがあります。
- when式の条件の構文
値 値1, 値2... , 値n in 範囲式 !in 範囲式 is 型 !is 型 else
- 値は、定数である必要はなくメソッドでも構いません。
- 式は、単式だけでなくブロック式
{...}
でも構いません。
引数のないwhen[編集]
whenの境界値が省略されると、true
が境界値に渡されたとみなされます。
- 引数のないwhenの例
fun main() { val a = 1 val b = 2 when { a == 0 -> println("a == 0") a == 1 && b == 1 -> println("a == 1 && b == 1") a == 1 && b == 2 -> println("a == 1 && b == 2") else -> println("else") } }
- 実行結果
a == 1 && b == 2
- ifを使った等価なコード
fun main() { val a = 1 val b = 2 if (a == 0) println("a == 0") else if (a == 1 && b == 1) println("a == 1 && b == 1") else if (a == 1 && b == 2) println("a == 1 && b == 2") else println("else") }
[TODO:境界値を省略した例、when文でループを脱出する例、enumな式が境界値に与えられた例]
繰返し処理[編集]
Kotlinには、while・do-while と for の3つの繰返し構文があります[2]。これらは文で、値を返すことはできません。
while[編集]
whileは、条件式が true の間、式を評価しつづけます[3]。
- 構文
while-stmt := while '(' 条件式 ')' 式
- 条件式は、Boolean 型でなければいけません。
- whileの例
fun main(args: Array<String>) { var i = 0 while (i < 5) { println(i) i += 1 } println("last = $i") }
- 実行結果
0 1 2 3 4 last = 5
do-while[編集]
do-whileは、条件式が true の間、式を評価しつづけるという意味では、whileと同じですが、条件式がループの最後にある、つまり条件が成立しなくても1周はループが回ることが異なります[4]。
- 構文
while-stmt := do 式 while '(' 条件式 ')'
- 条件式は、Boolean 型でなければいけません。
- do-whileの例
fun main() { var i = 0 do { i++ println("i = $i") } while (i < 0) }
- 実行結果
i = 1
for[編集]
Kotlinのforはw:foreach文タイプのループ構文で、C言語の for(;;) とは異なる構文です[5]。
- 構文
for (ループ変数 in コレクション) 式
- 範囲コレクションとforを組合せた例
fun main(args: Array<String>) { for (i in 1..5) println(i) println((1..5).javaClass.kotlin) }
- 実行結果
1 2 3 4 5 class kotlin.ranges.IntRange
- iの様なループ変数は、forがスコープになります。
- ここでは、型指定を省略しているので、型推論されコレクションの要素型になります。省略せず、
for (i : Int in 1..5) println(i)
- とすることもできます(ただし、異種コレクションだと要素型はUnion型になり宣言が複雑になるので、コードレビューの時に意図を明確にするなどの想起がない限り、型推論に任せるのが常です)。
- ループ変数は、varやvalを前置することができません。
- ループ変数には、代入できません。
for と等価な while[編集]
- for と等価な while
for (ループ変数 in コレクション) { println(ループ変数) } // は、以下と等価 val イテレーター = コレクション.iterator() while (イテレーター.hasNext()) { val ループ変数 = イテレーター.next() println(ループ変数) }