- Kotlin
Kotlin lateinitの初期化エラーの原因と直し方
kotlin.UninitializedPropertyAccessException: lateinit property repository has not been initialized は、lateinit var のプロパティに値を入れる前に、そのプロパティを読んだときのエラーです。 lateinit は「あとで必ず入れる」という約束で、Kotlin のコンパイラーは代入されたかを確認しません。約束が守られなかったとき、実行時にこの例外になります。
| 状況 | 直し方 |
|---|---|
| 代入する処理より前に呼ばれている | 呼ぶ順番を直す。または、作るときに値を渡す(コンストラクタ) |
| 入っていないこともありうる | lateinit をやめて、null 許容型(String?)にする |
| 最初に使うときに作ればよい | by lazy にする |
| 入ったかどうかを確かめたい | ::name.isInitialized(同じクラスの中から) |
確認環境:kotlinc-jvm 2.4.20、JDK 21(確認日 2026年10月10日)。以下のエラーとコンパイル結果は、すべて実際に実行して確かめました。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Kotlin lateinitの初期化エラーの原因と直し方」
再現1:代入する前に呼んだ
class UserService {
lateinit var repository: Repository
fun findName(id: Int): String = repository.find(id)
}
class Repository { fun find(id: Int) = "佐藤" }
fun main() {
UserService().findName(1) // repository に何も入れていない
}
Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property repository has not been initialized
at UserService.getRepository(A.kt:2)
at UserService.findName(A.kt:4)
at AKt.main(A.kt:34)
エラー文の lateinit property ○○ が、まだ入っていないプロパティの名前です。スタックトレースの get○○ の下の行が、読もうとした場所です。
代入してから呼ぶと、佐藤 と表示されました。
val service = UserService()
service.repository = Repository()
println(service.findName(1)) // 佐藤
再現2:親クラスのコンストラクタ(init)から呼ばれた
自分では「代入してから使っている」つもりでも、親クラスの init から、子クラスの処理が先に呼ばれることがあります。
open class Base {
init { setup() } // 親のコンストラクタの中で呼ぶ
open fun setup() {}
}
class Child : Base() {
lateinit var label: String
override fun setup() { println(label.length) }
}
fun main() { Child() }
Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property label has not been initialized
at Child.getLabel(A.kt:14)
at Child.setup(A.kt:15)
at Base.<init>(A.kt:10) ← 親のコンストラクタから呼ばれている
スタックトレースに <init>(コンストラクタ)があるなら、このパターンです。子クラスのプロパティは、親のコンストラクタが終わったあとに準備されます。親の init やコンストラクタから、open の関数を呼ばない設計にします。
直し方の使い分け
| 方法 | 書き方 | 向いている場面 |
|---|---|---|
| コンストラクタで受け取る | class UserService(private val repository: Repository) |
作るときに値が決まっている。一番安全(入れ忘れがコンパイルエラーになる) |
| null 許容型 | var title: String? = null |
入っていないことがありうる。使う側で ?. や ?: が必要 |
| by lazy | val config by lazy { 読み込み() } |
最初に使うときに作れば良い。val で書ける |
| lateinit | lateinit var x: Type |
フレームワーク(DI・テスト・Android)が、作ったあとに必ず入れる |
コンストラクタで受け取る
class UserService(private val repository: Repository) {
fun findName(id: Int): String = repository.find(id)
}
val service = UserService(Repository()) // 渡し忘れはコンパイルエラーになる
Spring などの DI(依存性の注入)でも、フィールドに @Autowired lateinit var で入れる代わりに、コンストラクタの引数で受け取る書き方ができます。入れ忘れが起きない形です。
by lazy:最初に使うときに作る
class Config {
val heavy: Map<String, String> by lazy {
println("初回だけ読み込む")
mapOf("url" to "jdbc:postgresql://localhost/appdb")
}
}
val c = Config()
println("作成")
println(c.heavy["url"])
println(c.heavy["url"])
作成
初回だけ読み込む ← 最初に使ったときだけ実行された
jdbc:postgresql://localhost/appdb
jdbc:postgresql://localhost/appdb
by lazy は、初期化の処理を自分で書けるときに使います。外から値を入れてもらう(DI・画面の部品)ものには使えません。
isInitialized:入ったかを確かめる
class Screen {
lateinit var title: String
fun show() {
if (::title.isInitialized) println("title=$title") else println("まだ設定されていない")
}
}
まだ設定されていない
title=一覧 ← title = "一覧" を入れたあと
isInitialized は、そのプロパティを宣言したクラスの中(または外側のクラス、同じファイルのトップレベル)からしか使えません(Kotlin 公式ドキュメント)。クラスの外から s::title.isInitialized と書くと、コンパイルエラーになりました。
error: backing field of 'var title: String' is not accessible at this point.
isInitialized があちこちに必要になるなら、「入っていないことがある」値なので、null 許容型にするほうが素直です。
lateinit にできない書き方(コンパイルエラー)
class Bad {
lateinit var count: Int // Int・Long・Boolean などの基本型
lateinit var name: String? // null 許容型
lateinit val id: String // val
}
error: 'lateinit' modifier is not allowed on properties of primitive types.
error: 'lateinit' modifier is not allowed on properties of a type with nullable upper bound.
error: 'lateinit' modifier is allowed only on mutable properties.
| 書きたいもの | 代わりの書き方 |
|---|---|
数値(Int など) |
初期値を入れる(var count = 0)、Int?、または by Delegates.notNull<Int>()(入れる前に読むと IllegalStateException: Property count should be initialized before get.) |
| null 許容型 | lateinit を外して = null |
| 読み取り専用 | val ... by lazy { }、またはコンストラクタで受け取る |
Android・テストでよくあるパターン
| 場面 | 起きやすい原因 | 直し方 |
|---|---|---|
| Android の Activity | onCreate で入れる前に、別のコールバックから読んだ |
入れる場所と、読む場所の順番を確かめる |
| Android の Fragment のビュー | ビューが破棄されたあとや、作られる前に読んだ | ビューの寿命の中だけで読む |
| JUnit のテスト | @BeforeEach で入れるはずが、メソッド名の付け間違い・アノテーションの付け忘れで呼ばれていない |
準備のメソッドが実行されているか確かめる |
| DI(Spring など) | DI が終わる前(コンストラクタや init)で読んだ |
コンストラクタで受け取る形にする |
どの場面でも、スタックトレースで「どこから読まれたか」を見れば、入れる処理より前に呼ばれた経路が分かります。
確認の手順(チェックリスト)
| 順番 | 確認すること | 方法 |
|---|---|---|
| 1 | どのプロパティか | エラー文の lateinit property ○○ |
| 2 | どこから読まれたか | スタックトレースの get○○ の下の行 |
| 3 | <init> から呼ばれていないか |
親クラスのコンストラクタ・init から open の関数を呼んでいないか |
| 4 | 入れる処理が実行されたか | @BeforeEach・onCreate・DI の設定 |
| 5 | 入っていないことがありうるか | ありうるなら null 許容型、作れるなら by lazy、渡せるならコンストラクタ |
よくある質問
Q. NullPointerException とは何が違いますか。
A. どちらも「値が無い」状態ですが、UninitializedPropertyAccessException は lateinit のプロパティに一度も代入されていないことを、プロパティの名前付きで知らせる例外です。どのプロパティが原因かがメッセージで分かります。
Q. lateinit を使わないほうが良いですか。
A. 入れ忘れが実行時まで分からないので、コンストラクタで受け取れるなら、そのほうが安全です。フレームワークがあとから必ず入れる場面(テストの準備、Android のビューなど)では、lateinit が向いています。
まとめ
lateinit varに入れる前に読んだときのエラー。メッセージにプロパティ名が出る- スタックトレースの
get○○の下の行が、読んだ場所。<init>があれば親のコンストラクタから呼ばれている - 作るときに決まるならコンストラクタ、無いことがあるなら null 許容型、最初に使うときに作るなら
by lazy isInitializedは宣言したクラスの中からしか使えない- 基本型・null 許容型・
valにはlateinitを付けられない
参考・出典
確認日はいずれも 2026年10月10日です。
- Kotlin ドキュメント「Properties」(Late-initialized properties and variables):https://kotlinlang.org/docs/properties.html
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Kotlin lateinitの初期化エラーの原因と直し方」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。