条件とループ
Kotlinは、プログラムのフローを制御するための柔軟なツールを提供します。if、when、およびループを使用して、条件に対する明確で表現力豊かなロジックを定義できます。
If 式
Kotlinで if を使用するには、チェックする条件を括弧 () 内に追加し、結果が真(true)の場合に実行するアクションを中括弧 {} 内に追加します。追加の分岐やチェックには else や else if を使用できます。
また、if を式として記述することもでき、その戻り値を直接変数に代入できます。この形式では、else ブランチが必須となります。if 式は、他の言語にある三項演算子(condition ? then : else)と同じ目的を果たします。
例えば以下のようになります:
fun main() {
val heightAlice = 160
val heightBob = 175
var taller = heightAlice
if (heightAlice < heightBob) taller = heightBob
// else ブランチを使用する場合
if (heightAlice > heightBob) {
taller = heightAlice
} else {
taller = heightBob
}
// if を式として使用する場合
taller = if (heightAlice > heightBob) heightAlice else heightBob
// else if を式として使用する場合:
val heightLimit = 150
val heightOrLimit = if (heightLimit > heightAlice) heightLimit else if (heightAlice > heightBob) heightAlice else heightBob
println("Taller height is $taller")
// Taller height is 175
println("Height or limit is $heightOrLimit")
// Height or limit is 175
}if 式の各ブランチはブロックにすることができ、その場合は最後の式の値が結果となります:
fun main() {
val heightAlice = 160
val heightBob = 175
val taller = if (heightAlice > heightBob) {
print("Choose Alice
")
heightAlice
} else {
print("Choose Bob
")
heightBob
}
println("Taller height is $taller")
}When 式と文
when は、複数の可能性のある値や条件に基づいてコードを実行する条件式です。これは Java や C、その他の言語における switch 文に似ています。when はその引数を評価し、いずれかのブランチ条件が満たされるまで、各ブランチと順番に結果を比較します。例えば:
fun main() {
val userRole = "Editor"
when (userRole) {
"Viewer" -> print("User has read-only access")
"Editor" -> print("User can edit content")
else -> print("User role is not recognized")
}
// User can edit content
}when は 式(expression) としても 文(statement) としても使用できます。式として使用する場合、when は後でコードで使用できる値を返します。文として使用する場合、when は結果を返さずにアクションを完了します。
| 式 (Expression) | 文 (Statement) |
kotlin | kotlin |
次に、when は対象(subject)の有無にかかわらず使用できます。どちらの場合も動作は同じです。対象を使用すると、何をチェックしているかが明確になるため、通常はコードの可読性と保守性が向上します。
対象 x がある場合 | 対象がない場合 |
kotlin | kotlin |
when をどのように使用するかによって、ブランチですべての可能なケースをカバーする必要があるかどうかが決まります。すべての可能なケースをカバーすることを、網羅的(exhaustive) であると言います。
文としての利用
when を文として使用する場合、すべての可能なケースをカバーする必要はありません。この例では、一部のケースがカバーされていないため、どのブランチも実行されません。しかし、エラーは発生しません。
fun main() {
val deliveryStatus = "OutForDelivery"
when (deliveryStatus) {
// すべてのケースがカバーされていない
"Pending" -> print("Your order is being prepared")
"Shipped" -> print("Your order is on the way")
}
}if と同様に、各ブランチはブロックにすることができ、その値はブロック内の最後の式の値となります。
式としての利用
when を式として使用する場合、すべての可能なケースを 必ず カバーしなければなりません。最初に一致したブランチの値が、式全体の値になります。すべてのケースをカバーしていない場合、コンパイラはエラーをスローします。
when 式に対象がある場合、else ブランチを使用してすべての可能なケースがカバーされていることを確認できますが、必須ではありません。例えば、対象が Boolean、enum クラス、sealed クラス、またはそれらの null 許容型のいずれかである場合、else ブランチなしですべてのケースをカバーできます。
import kotlin.random.Random
enum class Bit {
ZERO, ONE
}
fun getRandomBit(): Bit {
return if (Random.nextBoolean()) Bit.ONE else Bit.ZERO
}
fun main() {
val numericValue = when (getRandomBit()) {
// すべてのケースがカバーされているため、else ブランチは不要
Bit.ZERO -> 0
Bit.ONE -> 1
}
println("Random bit as number: $numericValue")
// Random bit as number: 0
}
when式を簡略化し、繰り返しを減らすために、コンテキスト依存解決(現在プレビュー中)を試してみてください。 この機能を使用すると、期待される型が既知である場合に、when式内で列挙型のエントリやシールクラスのメンバを使用する際に型名を省略できます。詳細については、Preview of context-sensitive resolution または関連する KEEP 提案 を参照してください。
when 式に対象が ない 場合、else ブランチが 必須 です。そうでないとコンパイラがエラーをスローします。else ブランチは、他のどのブランチ条件も満たされない場合に評価されます。
fun main() {
val localFileSize = 1200
val remoteFileSize = 1200
val message = when {
localFileSize > remoteFileSize -> "Local file is larger than remote file"
localFileSize < remoteFileSize -> "Local file is smaller than remote file"
else -> "Local and remote files are the same size"
}
println(message)
// Local and remote files are the same size
}その他の when の活用方法
when 式と文は、コードを簡略化し、複数の条件を処理し、型チェックを実行するためのさまざまな方法を提供します。
カンマを使用して、複数の条件を単一のブランチにグループ化します:
fun main() {
val ticketPriority = "High"
when (ticketPriority) {
"Low", "Medium" -> print("Standard response time")
else -> print("High-priority handling")
}
}ブランチ条件として true または false に評価される式を使用します:
fun main() {
val storedPin = "1234"
val enteredPin = 1234
when (enteredPin) {
// 式
storedPin.toInt() -> print("PIN is correct")
else -> print("Incorrect PIN")
}
}in または !in キーワードを使用して、値が 範囲(range) またはコレクションに含まれているか、含まれていないかをチェックします:
fun main() {
val x = 7
val validNumbers = setOf(15, 16, 17)
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
}is または !is キーワードを使用して、値の型をチェックします。スマートキャスト により、その型のメンバ関数やプロパティに直接アクセスできます:
fun hasPrefix(input: Any): Boolean = when (input) {
is String -> input.startsWith("ID-")
else -> false
}
fun main() {
val testInput = "ID-98345"
println(hasPrefix(testInput))
// true
}伝統的な if-else if チェーンの代わりに when を使用します。 対象がない場合、ブランチ条件は単なるブール式になります。true 条件を持つ最初のブランチが実行されます:
fun Int.isOdd() = this % 2 != 0
fun Int.isEven() = this % 2 == 0
fun main() {
val x = 5
val y = 8
when {
x.isOdd() -> print("x is odd")
y.isEven() -> print("y is even")
else -> print("x+y is odd")
}
// x is odd
}最後に、以下の構文を使用して対象を変数にキャプチャできます:
fun main() {
val message = when (val input = "yes") {
"yes" -> "You said yes"
"no" -> "You said no"
else -> "Unrecognized input: $input"
}
println(message)
// You said yes
}対象として導入された変数のスコープは、その when 式または文の本体に限定されます。
ガード条件
ガード条件を使用すると、when 式または文のブランチに複数の条件を含めることができ、複雑な制御フローをより明示的かつ簡潔に記述できます。ガード条件は、対象を持つ when で使用できます。
同一ブランチ内のプライマリ条件の後に、if で区切ってガード条件を配置します:
sealed interface Animal {
data class Cat(val mouseHunter: Boolean) : Animal
data class Dog(val breed: String) : Animal
}
fun feedDog() = println("Feeding a dog")
fun feedCat() = println("Feeding a cat")
fun feedAnimal(animal: Animal) {
when (animal) {
// プライマリ条件のみのブランチ
// animal が Dog の場合に feedDog() を呼び出す
is Animal.Dog -> feedDog()
// プライマリ条件とガード条件の両方を持つブランチ
// animal が Cat かつ mouseHunter でない場合に feedCat() を呼び出す
is Animal.Cat if !animal.mouseHunter -> feedCat()
// 上記の条件のいずれにも一致しない場合に "Unknown animal" を出力する
else -> println("Unknown animal")
}
}
fun main() {
val animals = listOf(
Animal.Dog("Beagle"),
Animal.Cat(mouseHunter = false),
Animal.Cat(mouseHunter = true)
)
animals.forEach { feedAnimal(it) }
// Feeding a dog
// Feeding a cat
// Unknown animal
}カンマで区切られた複数の条件がある場合は、ガード条件を使用できません。例えば:
0, 1 -> print("x == 0 or x == 1")単一の when 式または文の中で、ガード条件のあるブランチとないブランチを混在させることができます。 ガード条件のあるブランチのコードは、プライマリ条件とガード条件の両方が true と評価された場合にのみ実行されます。 プライマリ条件が一致しない場合、ガード条件は評価されません。
when 文はすべてのケースをカバーする必要がないため、else ブランチのない when 文でガード条件を使用し、どの条件も一致しない場合は、コードは実行されません。
文とは異なり、when 式はすべてのケースをカバーする必要があります。else ブランチのない when 式でガード条件を使用する場合、実行時エラーを避けるために、コンパイラはすべての可能なケースを処理することを要求します。
論理演算子 && (AND) または || (OR) を使用して、単一のブランチ内で複数のガード条件を組み合わせることができます。 混乱を避けるために、論理式を括弧で囲んでください:
when (animal) {
is Animal.Cat if (!animal.mouseHunter && animal.hungry) -> feedCat()
}ガード条件は else if もサポートしています:
when (animal) {
// `animal` が `Dog` であるかチェック
is Animal.Dog -> feedDog()
// `animal` が `Cat` かつ `mouseHunter` でないかチェックするガード条件
is Animal.Cat if !animal.mouseHunter -> feedCat()
// 上記の条件がどれも一致せず、かつ animal.eatsPlants が true の場合に giveLettuce() を呼び出す
else if animal.eatsPlants -> giveLettuce()
// 上記の条件のいずれにも一致しない場合に "Unknown animal" を出力する
else -> println("Unknown animal")
}For ループ
for ループを使用して、コレクション、配列、または 範囲(range) を反復処理します:
for (item in collection) print(item)for ループの本体は、中括弧 {} を使用したブロックにすることができます。
fun main() {
val shoppingList = listOf("Milk", "Bananas", "Bread")
println("Things to buy:")
for (item in shoppingList) {
println("- $item")
}
// Things to buy:
// - Milk
// - Bananas
// - Bread
}範囲 (Ranges)
数値の範囲を反復処理するには、.. および ..< 演算子を使用した 範囲式 を使用します:
fun main() {
println("Closed-ended range:")
for (i in 1..6) {
print(i)
}
// Closed-ended range:
// 123456
println("
Open-ended range:")
for (i in 1..<6) {
print(i)
}
// Open-ended range:
// 12345
println("
Reverse order in steps of 2:")
for (i in 6 downTo 0 step 2) {
print(i)
}
// Reverse order in steps of 2:
// 6420
}配列 (Arrays)
配列またはリストをインデックス付きで反復処理したい場合は、indices プロパティを使用できます:
fun main() {
val routineSteps = arrayOf("Wake up", "Brush teeth", "Make coffee")
for (i in routineSteps.indices) {
println(routineSteps[i])
}
// Wake up
// Brush teeth
// Make coffee
}または、標準ライブラリの .withIndex() 関数を使用することもできます:
fun main() {
val routineSteps = arrayOf("Wake up", "Brush teeth", "Make coffee")
for ((index, value) in routineSteps.withIndex()) {
println("The step at $index is \"$value\"")
}
// The step at 0 is "Wake up"
// The step at 1 is "Brush teeth"
// The step at 2 is "Make coffee"
}イテレータ (Iterators)
for ループは、イテレータ を提供するあらゆるものを反復処理します。コレクションはデフォルトでイテレータを提供しますが、範囲と配列はインデックスベースのループにコンパイルされます。
Iterator<> を返す iterator() という名前のメンバ関数または拡張関数を提供することで、独自のイテレータを作成できます。iterator() 関数は、next() 関数と Boolean を返す hasNext() 関数を持つ必要があります。
クラスに対して独自のイテレータを作成する最も簡単な方法は、Iterable<T> インターフェースを継承し、既存の iterator()、next()、hasNext() 関数をオーバーライドすることです。例えば:
class Booklet(val totalPages: Int) : Iterable<Int> {
override fun iterator(): Iterator<Int> {
return object : Iterator<Int> {
var current = 1
override fun hasNext() = current <= totalPages
override fun next() = current++
}
}
}
fun main() {
val booklet = Booklet(3)
for (page in booklet) {
println("Reading page $page")
}
// Reading page 1
// Reading page 2
// Reading page 3
}あるいは、ゼロから関数を作成することもできます。この場合、関数に operator キーワードを追加します:
class Booklet(val totalPages: Int) {
operator fun iterator(): Iterator<Int> {
return object {
var current = 1
operator fun hasNext() = current <= totalPages
operator fun next() = current++
}.let {
object : Iterator<Int> {
override fun hasNext() = it.hasNext()
override fun next() = it.next()
}
}
}
}
fun main() {
val booklet = Booklet(3)
for (page in booklet) {
println("Reading page $page")
}
// Reading page 1
// Reading page 2
// Reading page 3
}While ループ
while および do-while ループは、条件が満たされている間、その本体内のコードを継続的に実行します。 両者の違いは、条件をチェックするタイミングです:
whileは条件をチェックし、満たされている場合は本体のコードを実行し、その後再び条件チェックに戻ります。do-whileは本体のコードを実行してから条件をチェックします。条件が満たされていればループが繰り返されます。したがって、do-whileの本体は、条件にかかわらず少なくとも1回は実行されます。
while ループの場合、チェックする条件を括弧 () 内に、本体を中括弧 {} 内に記述します:
fun main() {
var carsInGarage = 0
val maxCapacity = 3
while (carsInGarage < maxCapacity) {
println("Car entered. Cars now in garage: ${++carsInGarage}")
}
// Car entered. Cars now in garage: 1
// Car entered. Cars now in garage: 2
// Car entered. Cars now in garage: 3
println("Garage is full!")
// Garage is full!
}do-while ループの場合、括弧 () 内の条件チェックの前に、まず中括弧 {} 内に本体を記述します:
import kotlin.random.Random
fun main() {
var roll: Int
do {
roll = Random.nextInt(1, 7)
println("Rolled a $roll")
} while (roll != 6)
// Rolled a 2
// Rolled a 6
println("Got a 6! Game over.")
// Got a 6! Game over.
}ループ内の break と continue
Kotlinは、ループ内での伝統的な break および continue 演算子をサポートしています。リターンとジャンプ を参照してください。
