Kotlin

Kotlin入門

変数、分岐、繰り返し、コレクション、関数、null安全を、実行結果付きで学びます。

対象:Kotlin 2系・JDK 17以降のJVMコンソールアプリを対象にします。Android専用のAPIは使いません。

01

Kotlinとは

KotlinはAndroidアプリやサーバーの開発などで使われるプログラミング言語です。この講座はJVM(Javaの実行基盤)上で動くコンソールアプリで学びます。Kotlinの文法と、Androidの画面・ライフサイクルなどは別の学習項目です。

プログラミング経験は不要です。実行環境 → 基本文法 → null安全 → クラス → 保存・テストの順に進めます。以下では正式名称のKotlinを使います。
02

準備と最初の実行

公式の作成手順を参考にIntelliJ IDEAでKotlinのプロジェクトを作成します。JDKに17以降を選び、生成されたMain.ktを次の内容に置き換えてmainの実行ボタンを押します。JDKはコンパイルと実行に必要な開発環境です。

Main.ktKOTLIN
fun main() {
    println("こんにちは、Kotlin!")
}
実行結果OUTPUT
こんにちは、Kotlin!

ターミナルを使う場合は、JDKに加えてKotlinコンパイラーの導入手順に従ってkotlincを設定します。UTF-8でMain.ktを保存したフォルダーで、1行ずつ実行します。

コンパイルと実行COMMAND
java -version
kotlinc -version
kotlinc Main.kt -include-runtime -d study.jar
java -jar study.jar

-include-runtime はKotlinの実行ライブラリをJARに含め、-d は出力先を指定します。JARを動かすJavaは別途必要です。以下は各例でMain.ktの全体を置き換えて、再コンパイル・実行します。

「コマンドが見つからない」はPATHとインストールを確認します。「Unable to access jarfile」はコンパイルが成功してstudy.jarが作成されたかを確認します。IDEにpackage宣言がある場合、入門では削除して以下と揃えます。
03

mainと基本の書き方

fun main() が実行の入口です。{ } 内は上から順番に実行されます。println は末尾に改行を付け、print は改行しません。コメントは // で書きます。

Main.kt(全体を置き換え)KOTLIN
fun main() {
    // 処理の順番を確かめる
    print("合計: ")
    println(10 + 20)
    println("終了")
}
実行結果OUTPUT
合計: 30
終了
04

val・varとデータ型

val は再代入できない変数、var は再代入できる変数です。型はコロンの後に指定し、省略すると推論されます。Intは整数、Doubleは小数、Stringは文字列、Booleanは真偽値です。$名前 または ${式} で文字列に埋め込みます。

Main.kt(全体を置き換え)KOTLIN
fun main() {
    val name: String = "Mity"
    var age: Int = 20
    val height: Double = 170.5
    val isStudent: Boolean = true
    age += 1
    println("${name}さんは${age}歳です")
    println(height)
    println(isStudent)
}
実行結果OUTPUT
Mityさんは21歳です
170.5
true

まずvalで書き、変更する必要がある値だけvarにします。IntへStringを代入するなど型が合わないコードはコンパイル時に検出されます。

05

入力と型変換

readlnOrNull() で1行を読みます。?. は入力がある場合だけ処理し、toIntOrNull() は整数にできない場合にnullを返します。nullは「値がない」ことを表します。

Main.kt(全体を置き換え)KOTLIN
fun main() {
    println("年齢を入力してください")
    val age = readlnOrNull()?.toIntOrNull()
    if (age != null && age >= 0 && age <= 130) {
        println("来年は${age + 1}歳です")
    } else {
        println("0〜130の整数を入力してください")
    }
}
実行例(20を入力してEnter)OUTPUT
年齢を入力してください
20
来年は21歳です

空文字列、"二十"、-1では案内メッセージが表示されます。入力を使えない環境では val age = "20".toIntOrNull() として試します。

06

計算・比較・論理演算

+ - * / は四則演算、% は余りです。Int同士の割り算は整数になり、小数が必要なら toDouble() で変換します。== は等しいか、!= は異なるかを調べます。

Main.kt(全体を置き換え)KOTLIN
fun main() {
    val price = 500
    val count = 3
    println(price * count)
    println(7 / 2)
    println(7.toDouble() / 2)
    println(7 % 2)
    println(price == 500 && count >= 1)
    println(count == 0 || price > 0)
    println(!(count != 3))
}
実行結果OUTPUT
1500
3
3.5
1
true
true
true

&& は両方が真、|| はどちらかが真、! は真偽の反転です。代入の=と比較の==を区別します。整数を0で割ると例外になるため、割る数を先に検証します。

07

ifとwhen

ifは条件ごとに処理を分けます。Kotlinのifは値も返せるため、結果を変数に代入できます。値として使う場合はelseも必要です。

Main.kt(全体を置き換え)KOTLIN
fun main() {
    val score = 82
    val result = if (score >= 80) {
        "よくできました"
    } else if (score >= 60) {
        "合格"
    } else {
        "再挑戦"
    }
    println(result)
}
実行結果OUTPUT
よくできました

when は複数の候補を順に調べ、最初に一致した枝を実行します。else はその他の場合です。各枝を終えるbreakは不要です。

Main.kt(全体を置き換え)KOTLIN
fun main() {
    val day = "土"
    val label = when (day) {
        "土", "日" -> "休日"
        else -> "平日"
    }
    println(label)
}
実行結果OUTPUT
休日
08

繰り返しと範囲

1..3 は1から3まで、0 until 3 は0から2までです。whileは条件が真の間繰り返します。条件が変わる行を忘れると止まらなくなります。

Main.kt(全体を置き換え)KOTLIN
fun main() {
    for (number in 1..3) { println(number) }
    var count = 2
    while (count > 0) {
        println("残り $count")
        count -= 1
    }
}
実行結果OUTPUT
1
2
3
残り 2
残り 1

continue は今回だけを飛ばし、break はループ全体を終了します。

Main.kt(全体を置き換え)KOTLIN
fun main() {
    for (number in 1..5) {
        if (number == 2) continue
        if (number == 4) break
        println(number)
    }
}
実行結果OUTPUT
1
3
09

Listと変更可能なリスト

listOf は読み取り用のList、mutableListOf は追加・削除できるMutableListを作ります。位置は0からです。getOrNull は範囲外ならnullを返し、?: で代わりの値を決められます。

Main.kt(全体を置き換え)KOTLIN
fun main() {
    val fruits = mutableListOf("りんご", "みかん")
    fruits.add("もも")
    println(fruits[0])
    println(fruits.size)
    println(fruits.getOrNull(10) ?: "見つかりません")
    for ((index, fruit) in fruits.withIndex()) {
        println("${index + 1}: $fruit")
    }
}
実行結果OUTPUT
りんご
3
見つかりません
1: りんご
2: みかん
3: もも
valは変数の再代入を防ぎますが、MutableListの中身の変更は防ぎません。またListは読み取り専用のインターフェースであり、同じ実体を別の参照から変更できないという保証ではありません。
10

Map・Set・配列

Mapはキーと値の組、Setは重複しない値の集まりです。to でキーと値の組を作ります。配列は arrayOf で作り、要素数は作成後に変えられません。

Main.kt(全体を置き換え)KOTLIN
fun main() {
    val stocks = mapOf("りんご" to 5, "みかん" to 8)
    println(stocks["りんご"] ?: 0)
    println(stocks["もも"] ?: 0)
    val tags = setOf("Kotlin", "Web", "Kotlin")
    println(tags.sorted())
    val scores = arrayOf(60, 80)
    scores[0] = 70
    println(scores.joinToString(", "))
}
実行結果OUTPUT
5
0
[Kotlin, Web]
70, 80

MapやSetを変更したい場合はmutableMapOf、mutableSetOfを使います。集合や辞書の順序に依存する表示は避け、必要なときは並べ替えます。

11

関数とデフォルト引数

fun で関数を定義します。引数リストの後の : Int が戻り値の型、returnが返す値です。countの初期値を1にすると、呼び出し時に省略できます。

Main.kt(全体を置き換え)KOTLIN
fun total(price: Int, count: Int = 1): Int {
    return price * count
}
fun main() {
    println(total(price = 500, count = 3))
    println(total(500))
}
実行結果OUTPUT
1500
500

名前付き引数は price = 500 のように書きます。値を返さない関数はUnit型です。関数内の変数は基本的にその中でだけ使えます。

12

null安全

String? はnullを許す型で、String は通常nullを持てません。?. は安全なアクセス、?: はnullの場合の代替値です。ifでnullでないと確認すると、その範囲では通常の型として扱えます。

Main.kt(全体を置き換え)KOTLIN
fun main() {
    val nickname: String? = null
    println(nickname?.length ?: 0)
    val text: String? = "Kotlin"
    if (text != null) { println(text.length) }
    println("abc".toIntOrNull() ?: -1)
}
実行結果OUTPUT
0
6
-1
!! はnullでないと強制する指定で、nullなら例外になります。コンパイルエラーを消すために付けるのではなく、値がない場合の動きを決めましょう。
13

文字列操作とラムダ式

trim() は前後の空白を除き、uppercase() は英字を大文字にします。split は区切り文字で分割します。どれも結果を戻り値として受け取ります。

Main.kt(全体を置き換え)KOTLIN
fun main() {
    val text = " Kotlin "
    println(text.trim())
    println(text.trim().uppercase())
    println("りんご,みかん".split(",").size)
}
実行結果OUTPUT
Kotlin
KOTLIN
2

ラムダ式は処理を値として渡す書き方です。filter は条件に合う要素を残し、map は変換します。score -> のscoreが処理中の要素です。

Main.kt(全体を置き換え)KOTLIN
fun main() {
    val scores = listOf(40, 70, 90)
    val passed = scores.filter { score -> score >= 60 }
    val doubled = passed.map { score -> score * 2 }
    println(passed)
    println(doubled)
}
実行結果OUTPUT
[70, 90]
[140, 180]
14

クラスとデータクラス

クラスは値と操作をまとめます。data class はデータ保持に向き、表示や比較などを自動生成します。Product(...) でインスタンスを作ります。

Main.kt(全体を置き換え)KOTLIN
data class Product(val name: String, val price: Int) {
    fun total(count: Int): Int = price * count
}
fun main() {
    val pen = Product("ペン", 120)
    println(pen)
    println(pen.total(3))
}
実行結果OUTPUT
Product(name=ペン, price=120)
360

メソッドの = price * count は1つの式を返す関数の省略形です。通常のクラス、状態の保護、copyの注意点は発展ページで確認します。

15

例外を受け取る

toInt() は変換できないと例外を投げます。try の失敗を catch で受け取れます。入力の数値変換にはtoIntOrNullが便利ですが、ファイル処理など例外を報告するAPIではtry-catchが必要です。

Main.kt(全体を置き換え)KOTLIN
fun main() {
    try {
        println("abc".toInt())
    } catch (error: NumberFormatException) {
        println("整数に変換できませんでした")
    } finally {
        println("確認終了")
    }
}
実行結果OUTPUT
整数に変換できませんでした
確認終了

finallyは成功・失敗のどちらでも実行される後片付けです。catchでは予期した例外の型を指定し、原因の異なる不具合を隠さないようにします。

16

練習問題

問題1:numberを7として偶数・奇数を表示します。0、8に変更しても確かめてください。

解答例と結果を確認する

まず自分で書いてから、処理の順番と結果を比べましょう。

Main.kt(全体を置き換え)KOTLIN
fun main() {
    val number = 7
    println(if (number % 2 == 0) "偶数" else "奇数")
}
実行結果OUTPUT
奇数

問題2:[60, 80, 100]の平均を求め、空のリストなら「データなし」と表示します。

解答例と結果を確認する

まず自分で書いてから、処理の順番と結果を比べましょう。

Main.kt(全体を置き換え)KOTLIN
fun main() {
    val scores = listOf(60, 80, 100)
    if (scores.isEmpty()) {
        println("データなし")
    } else {
        println(scores.sum().toDouble() / scores.size)
    }
}
実行結果OUTPUT
80.0

問題3:Mapから在庫を取得する関数を作り、未登録なら0を返します。

解答例と結果を確認する

まず自分で書いてから、処理の順番と結果を比べましょう。

Main.kt(全体を置き換え)KOTLIN
fun stock(name: String): Int {
    val stocks = mapOf("りんご" to 5, "みかん" to 8)
    return stocks[name] ?: 0
}
fun main() {
    println(stock("みかん"))
    println(stock("もも"))
}
実行結果OUTPUT
8
0
17

次に学ぶこと

基礎の次は、以下の4ページを順に進めましょう。各ページに手順、コード例、確認する結果と練習問題があります。

公式ドキュメントで確認する:Kotlinのコンソールアプリ / コマンドラインコンパイラー / 基本文法