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期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?