Kotlinのクラスについて

採用はこちら

Kotlinのクラスは、Javaのクラス設計を踏襲しつつも、簡潔性・安全性・設計の明示性を大幅に強化した言語仕様になっています。

本記事では、「間違った理解が広まりやすいポイント」を修正した形で、Kotlinのクラスを体系的に解説します。

目次

Kotlinにおけるクラスの基本的な位置づけ

Kotlinのクラスは以下をまとめる単位です。

  • データ(プロパティ)
  • 振る舞い(関数)
  • 初期化ロジック
  • 可視性・継承ルール

Javaと同じ「クラス」ではありますが、Kotlinでは以下の思想が強く反映されています。

  • ボイラープレートの削減
  • 不変(immutable)を基本とする設計
  • 意図しない継承・変更の防止
  • null安全を言語レベルで保証

クラス定義の最小構文

class Person {
    val name: String = "Taro"
    var age: Int = 20

    fun greet() {
        println("こんにちは、$name です")
    }
}

補足

  • Kotlinでは変数ではなく プロパティ という概念を使う
  • getter / setter は自動生成される
  • Javaのようにフィールドを直接操作する設計は推奨されない

プライマリコンストラクタ(Kotlinの核)

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

この1行で以下が同時に定義されます。

  • コンストラクタ
  • プロパティ
  • getter / setter

重要な理解ポイント

  • コンストラクタ引数に val / var を付けると そのままプロパティになる
  • Kotlinでは「データ保持クラス」を非常に簡潔に書ける

initブロック(初期化処理)

class Person(val name: String, age: Int) {

    var age: Int = age

    init {
        println("Personクラスが生成されました")
    }
}

initの特徴

  • プライマリコンストラクタ実行時に必ず呼ばれる
  • 複数記述でき、上から順に実行される
  • 初期化時の検証・ログ出力に使われることが多い

セカンダリコンストラクタ

class Person(val name: String) {

    var age: Int = 0

    constructor(name: String, age: Int) : this(name) {
        this.age = age
    }
}

ルール

  • セカンダリコンストラクタは必ずプライマリコンストラクタを呼ぶ
  • 初期化経路が一貫するため、設計ミスを防ぎやすい

プロパティとバッキングフィールドの正確な理解

Kotlinでは基本的に プロパティ を使いますが、内部的には 必要な場合のみバッキングフィールド が生成されます。

var age: Int = 0
    set(value) {
        require(value >= 0)
        field = value
    }

field についての重要事項

  • fieldバッキングフィールドが存在する場合のみ使用可能
  • すべてのプロパティにバッキングフィールドがあるわけではない

バッキングフィールドを持たない例

val now: Long
    get() = System.currentTimeMillis()

この場合、field は存在せず、使用できません。

可視性修飾子(アクセス制御)

修飾子説明
publicどこからでもアクセス可能(デフォルト)
privateクラス内のみ
protectedサブクラスのみ
internal同一モジュール内のみ
class Sample {
    private val secret = "秘密"
    internal val moduleValue = "モジュール内"
}

継承と open / override

Kotlinでは クラス・関数ともにデフォルトで継承不可 です。

open class Animal {
    open fun speak() {
        println("鳴く")
    }
}

class Dog : Animal() {
    override fun speak() {
        println("ワン")
    }
}

設計思想

  • 継承は「設計者が明示的に許可」するもの
  • Javaの「不用意なオーバーライド事故」を防ぐ

abstractクラス

abstract class Shape {
    abstract fun area(): Double
}
  • インスタンス化不可
  • 抽象メンバは必ず override が必要

data class(データ保持専用クラス)

data class User(val id: Int, val name: String)

自動生成されるもの

  • toString()
  • equals()
  • hashCode()
  • copy()
  • componentN()(分解宣言用)

重要な注意点

  • 対象になるのは 主コンストラクタの val / var プロパティのみ
  • 主コンストラクタ外のプロパティは等価性に含まれない

sealed class(制限付き継承)

sealed class Result {
    data class Success(val data: String) : Result()
    data class Error(val message: String) : Result()
}

特徴

  • 継承できる型が制限される
  • 状態・結果表現に向いている

when式との関係(重要)

fun handle(result: Result) = when (result) {
    is Result.Success -> println(result.data)
    is Result.Error -> println(result.message)
}
  • 全サブクラスを網羅している場合のみ else を省略できる
  • 網羅されていない場合はコンパイルエラーまたは else が必要

object(シングルトン)

object AppConfig {
    const val VERSION = "1.0.0"
}
  • 言語レベルでシングルトンを保証
  • JVM上でも通常はスレッドセーフに初期化される
  • static を使わずに済む

companion object(static的な存在)

class Util {
    companion object {
        fun sayHello() = println("Hello")
    }
}
Util.sayHello()

正確な理解

  • 呼び出し方・用途は Javaの static に近い
  • ただし言語的には「クラスに紐づくオブジェクト」
  • Java相互運用では @JvmStatic により挙動を調整できる

Kotlinクラス設計の本質

Kotlinのクラス設計は一貫して以下を目指しています。

  • 明示的であること
  • 不変性を重視すること
  • バグが入り込む余地を減らすこと
  • データとロジックを整理すること

そのため実務では、

  • val を基本にする
  • data classsealed class を積極的に使う
  • 継承より合成を優先する

という設計が自然に導かれます。

以上、Kotlinのクラスについてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次