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 classとsealed classを積極的に使う- 継承より合成を優先する
という設計が自然に導かれます。
以上、Kotlinのクラスについてでした。
最後までお読みいただき、ありがとうございました。








