Kotlin

#14
TIOBE#23
PYPL#13
GitHub#19
RedMonk#14
IEEESpectrum#17
JetBrains#13
プログラミング言語オブジェクト指向関数型JVMAndroidサーバーサイドInterop

プログラミング言語

Kotlin

概要

Kotlinは、JetBrainsが開発したJVM上で動作するプログラミング言語です。

詳細

Kotlinは2011年にJetBrainsによって開発が開始され、2016年に正式リリースされたプログラミング言語です。Java仮想マシン(JVM)上で動作し、Javaとの100%相互運用性を持ちながら、より簡潔で安全なコードを書くことができます。Null安全性、型推論、拡張関数、データクラスなどの現代的な機能を提供しています。2017年にGoogleがAndroidの公式開発言語として採用し、現在は「Kotlin First」として推奨されています。サーバーサイド開発、Web開発、ネイティブアプリ開発など、幅広い分野で活用されています。

書き方の例

Hello World

// 基本的な出力
fun main() {
    println("Hello, World!")
    
    // 変数を使った出力
    val message = "こんにちは、Kotlin!"
    println(message)
    
    // 文字列テンプレートを使った出力
    val name = "太郎"
    val age = 25
    println("私の名前は${name}で、${age}歳です。")
}

// 従来の関数形式
fun main(args: Array<String>) {
    println("Hello from main function!")
}

変数と型

fun main() {
    // 読み取り専用変数(val)
    val name = "太郎" // 型推論
    val age: Int = 25 // 明示的な型指定
    
    // 可変変数(var)
    var score = 85
    score = 90 // 変更可能
    
    // 基本型
    val height: Double = 175.5
    val isActive: Boolean = true
    val character: Char = 'K'
    
    // Null許容型
    val nullableName: String? = null
    val nonNullName: String = "田中"
    
    // 安全呼び出し演算子
    val length = nullableName?.length
    println("Null許容文字列の長さ: $length")
    
    // エルビス演算子
    val displayName = nullableName ?: "名前なし"
    println("表示名: $displayName")
    
    // 非Null表明演算子(!!)
    val definitelyNotNull = nonNullName!!
    println("確実に非Null: $definitelyNotNull")
    
    // 文字列テンプレート
    println("名前: $name, 年齢: $age, 身長: ${height}cm")
    println("アクティブ: ${if (isActive) "はい" else "いいえ"}")
}

関数

// 基本的な関数
fun add(a: Int, b: Int): Int {
    return a + b
}

// 式関数
fun multiply(a: Int, b: Int) = a * b

// デフォルト引数
fun greet(name: String, prefix: String = "さん") = "こんにちは、$name$prefix"

// 名前付き引数
fun createUser(name: String, age: Int, email: String = "") {
    println("ユーザー作成: $name ($age歳) - $email")
}

// 可変長引数
fun sum(vararg numbers: Int): Int {
    return numbers.sum()
}

// 高階関数
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

// 拡張関数
fun String.isValidEmail(): Boolean {
    return this.contains("@") && this.contains(".")
}

fun main() {
    // 関数の呼び出し
    println("5 + 3 = ${add(5, 3)}")
    println("4 × 6 = ${multiply(4, 6)}")
    
    // デフォルト引数
    println(greet("田中"))
    println(greet("山田", "様"))
    
    // 名前付き引数
    createUser(name = "佐藤", age = 30, email = "[email protected]")
    createUser(age = 25, name = "高橋") // 順序を変更可能
    
    // 可変長引数
    println("合計: ${sum(1, 2, 3, 4, 5)}")
    
    // 高階関数
    val result = calculate(10, 5) { x, y -> x - y }
    println("10 - 5 = $result")
    
    // ラムダ式
    val numbers = listOf(1, 2, 3, 4, 5)
    val doubled = numbers.map { it * 2 }
    println("倍数: $doubled")
    
    // 拡張関数
    val email = "[email protected]"
    println("$email は有効なメール: ${email.isValidEmail()}")
}

クラスとオブジェクト

// 基本的なクラス
class Person(val name: String, var age: Int) {
    // プロパティ
    val id: String = generateId()
    
    // メソッド
    fun getInfo(): String = "$name ($age歳) - ID: $id"
    
    // セカンダリコンストラクタ
    constructor(name: String) : this(name, 0)
    
    private fun generateId(): String = "ID_${name.hashCode()}"
}

// データクラス
data class User(
    val id: Int,
    val name: String,
    val email: String
)

// シールドクラス
sealed class Result {
    data class Success(val data: String) : Result()
    data class Error(val message: String) : Result()
    object Loading : Result()
}

// インターフェース
interface Drawable {
    fun draw()
    
    // デフォルト実装
    fun info() = "これは描画可能なオブジェクトです"
}

// 継承
abstract class Shape : Drawable {
    abstract val area: Double
    
    override fun info() = "これは図形です: 面積 = $area"
}

class Circle(private val radius: Double) : Shape() {
    override val area: Double
        get() = Math.PI * radius * radius
    
    override fun draw() {
        println("半径 $radius の円を描画します")
    }
}

// オブジェクト宣言(シングルトン)
object DatabaseManager {
    fun connect(): String = "データベースに接続しました"
    fun disconnect(): String = "データベースから切断しました"
}

fun main() {
    // クラスのインスタンス化
    val person1 = Person("田中太郎", 25)
    val person2 = Person("山田花子")
    
    println(person1.getInfo())
    println(person2.getInfo())
    
    // データクラス
    val user = User(1, "佐藤", "[email protected]")
    println("ユーザー: $user")
    
    // データクラスのコピー
    val updatedUser = user.copy(email = "[email protected]")
    println("更新されたユーザー: $updatedUser")
    
    // 分解宣言
    val (id, name, email) = user
    println("ID: $id, 名前: $name, メール: $email")
    
    // シールドクラス
    val results = listOf(
        Result.Success("データを取得しました"),
        Result.Error("ネットワークエラー"),
        Result.Loading
    )
    
    results.forEach { result ->
        when (result) {
            is Result.Success -> println("成功: ${result.data}")
            is Result.Error -> println("エラー: ${result.message}")
            is Result.Loading -> println("読み込み中...")
        }
    }
    
    // 継承とポリモーフィズム
    val circle = Circle(5.0)
    circle.draw()
    println(circle.info())
    
    // オブジェクト宣言
    println(DatabaseManager.connect())
    println(DatabaseManager.disconnect())
}

コレクションと関数型プログラミング

fun main() {
    // リスト
    val numbers = listOf(1, 2, 3, 4, 5)
    val mutableNumbers = mutableListOf(1, 2, 3)
    mutableNumbers.add(4)
    
    // セット
    val uniqueNumbers = setOf(1, 2, 2, 3, 3)
    println("ユニークな数値: $uniqueNumbers")
    
    // マップ
    val ages = mapOf("田中" to 25, "山田" to 30, "佐藤" to 28)
    val mutableAges = mutableMapOf<String, Int>()
    mutableAges["高橋"] = 35
    
    // 関数型メソッド
    val evenNumbers = numbers.filter { it % 2 == 0 }
    println("偶数: $evenNumbers")
    
    val doubled = numbers.map { it * 2 }
    println("倍数: $doubled")
    
    val sum = numbers.reduce { acc, n -> acc + n }
    println("合計: $sum")
    
    val sumWithInitial = numbers.fold(10) { acc, n -> acc + n }
    println("初期値付き合計: $sumWithInitial")
    
    // グループ化
    val words = listOf("apple", "banana", "apricot", "cherry", "avocado")
    val groupedByFirstLetter = words.groupBy { it.first() }
    println("最初の文字でグループ化: $groupedByFirstLetter")
    
    // チェーンメソッド
    val result = words
        .filter { it.startsWith("a") }
        .map { it.uppercase() }
        .sorted()
    println("処理結果: $result")
    
    // シーケンス(遅延評価)
    val largeNumbers = generateSequence(1) { it + 1 }
        .filter { it % 2 == 0 }
        .take(5)
        .toList()
    println("シーケンス結果: $largeNumbers")
}

スコープ関数

data class Person(var name: String, var age: Int)

fun main() {
    // let - オブジェクトの変換とNull安全性
    val person: Person? = Person("田中", 25)
    person?.let {
        println("${it.name}${it.age} 歳です")
    }
    
    val length = "Hello, Kotlin"?.let {
        println("文字列: $it")
        it.length // 戻り値
    }
    println("文字列の長さ: $length")
    
    // run - オブジェクトの設定と結果の計算
    val message = Person("山田", 30).run {
        age += 1
        "来年 $name$age 歳になります"
    }
    println(message)
    
    // with - オブジェクトのメソッドを連続呼び出し
    val numbers = mutableListOf<Int>()
    with(numbers) {
        add(1)
        add(2)
        add(3)
        println("リスト: $this")
    }
    
    // apply - オブジェクトの設定
    val person2 = Person("", 0).apply {
        name = "佐藤"
        age = 28
    }
    println("設定後: $person2")
    
    // also - 追加の処理
    val person3 = Person("高橋", 35).also {
        println("作成されたperson: ${it.name}")
    }
    
    // takeIf/takeUnless - 条件付き処理
    val adult = person3.takeIf { it.age >= 18 }
    println("成人: $adult")
    
    val notTeenager = person3.takeUnless { it.age in 13..19 }
    println("ティーンエイジャーではない: $notTeenager")
}

例外処理

// カスタム例外クラス
class ValidationException(message: String) : Exception(message)

fun validateAge(age: Int) {
    if (age < 0) {
        throw ValidationException("年齢は0以上である必要があります")
    }
    if (age > 150) {
        throw ValidationException("年齢は150以下である必要があります")
    }
}

fun divide(a: Int, b: Int): Double {
    if (b == 0) {
        throw ArithmeticException("ゼロで除算はできません")
    }
    return a.toDouble() / b
}

fun main() {
    // try-catch文
    try {
        val result = divide(10, 2)
        println("10 ÷ 2 = $result")
    } catch (e: ArithmeticException) {
        println("算術エラー: ${e.message}")
    }
    
    // 複数の例外をキャッチ
    try {
        validateAge(-5)
    } catch (e: ValidationException) {
        println("検証エラー: ${e.message}")
    } catch (e: Exception) {
        println("予期しないエラー: ${e.message}")
    } finally {
        println("cleanup処理")
    }
    
    // try式(値を返す)
    val safeDivision = try {
        divide(10, 0)
    } catch (e: ArithmeticException) {
        0.0 // デフォルト値
    }
    println("安全な除算結果: $safeDivision")
    
    // runCatching(関数型アプローチ)
    val result = runCatching {
        divide(15, 3)
    }
    
    result
        .onSuccess { println("成功: $it") }
        .onFailure { println("失敗: ${it.message}") }
    
    // getOrElse
    val safeResult = runCatching { divide(10, 0) }
        .getOrElse { -1.0 }
    println("デフォルト値付き結果: $safeResult")
    
    // fold
    val foldResult = runCatching { validateAge(25) }
        .fold(
            onSuccess = { "年齢検証成功" },
            onFailure = { "年齢検証失敗: ${it.message}" }
        )
    println(foldResult)
}

Coroutines(協調的並行処理)

import kotlinx.coroutines.*

// suspend関数
suspend fun fetchUserData(userId: Int): String {
    delay(1000) // 非同期待機(1秒)
    return "ユーザー$userId のデータ"
}

suspend fun fetchUserPosts(userId: Int): List<String> {
    delay(800) // 非同期待機(0.8秒)
    return listOf("投稿1", "投稿2", "投稿3")
}

fun main() = runBlocking {
    println("=== 基本的なCoroutine ===")
    
    // 基本的なlaunch
    val job = launch {
        repeat(3) {
            println("Coroutine実行中: $it")
            delay(500)
        }
    }
    
    delay(1000)
    job.cancel()
    println("Jobをキャンセルしました")
    
    println("\n=== async/await ===")
    
    // async/awaitによる並行処理
    val userData = async { fetchUserData(1) }
    val userPosts = async { fetchUserPosts(1) }
    
    println("ユーザーデータ: ${userData.await()}")
    println("ユーザー投稿: ${userPosts.await()}")
    
    println("\n=== エラーハンドリング ===")
    
    // エラーハンドリング
    try {
        val errorJob = async {
            delay(100)
            throw Exception("何かがうまくいかなかった")
        }
        errorJob.await()
    } catch (e: Exception) {
        println("エラーをキャッチ: ${e.message}")
    }
    
    println("\n=== CoroutineScope ===")
    
    // カスタムスコープ
    val customScope = CoroutineScope(Dispatchers.Default + Job())
    
    customScope.launch {
        println("カスタムスコープで実行")
        delay(100)
        println("カスタムスコープ完了")
    }
    
    delay(200) // 完了を待機
    customScope.cancel() // スコープをキャンセル
    
    println("プログラム終了")
}

バージョン

バージョン リリース日 主な新機能
Kotlin 2.1 2024-11 Kotlin Multiplatform improvements, New K2 compiler
Kotlin 2.0 2024-05 K2 compiler stable, Kotlin Multiplatform stable
Kotlin 1.9 2023-07 Enum entries function, Data object declarations
Kotlin 1.8 2023-02 Recursive generic types, @JvmMultifileClass improvements
Kotlin 1.7 2022-06 Kotlin Multiplatform Mobile Alpha, Builder inference
Kotlin 1.6 2021-11 Stable exhaustive when, Kover code coverage
Kotlin 1.5 2021-05 JVM IR backend stable, Value classes

参考ページ

公式ドキュメント

学習リソース

開発ツール