はじめに
Kotlin は「Null安全」を重視する言語です。
しかし時には「あとで初期化したい」場面もあります。
例えば:
- Androidの
Activity
内でonCreate()
で初期化するView
やViewModel
- DI(依存性注入)であとから代入されるオブジェクト
- テストコードで後からモックを設定するフィールド
こうした「最初は未初期化、あとで代入する」パターンを安全に扱うのが
lateinit
修飾子です。
基本構文
lateinit var name: String
lateinit
は 「後で初期化する」ことをコンパイラに宣言する修飾子 です。
通常の変数は宣言時に初期化が必要ですが、lateinit
によってそれを遅延できます。
class Person {
lateinit var name: String
fun init() {
name = "Anna"
}
fun sayHello() {
println("Hello, $name")
}
}
fun main() {
val person = Person()
person.init()
person.sayHello() // Hello, Anna
}
普通の var
との違い
var name: String // ❌ エラー: 初期化されていない
lateinit var name: String // ✅ OK(後で代入予定)
Kotlinはすべての非null変数に対して「必ず初期化」を求めます。
しかし lateinit
を使うと「初期化をあとに回す」ことが許されます。
使用条件(制約)
条件 | 内容 |
---|---|
✅ 修飾できるのは var (再代入可能)だけ |
val には使えない |
✅ 非null型 (String , Int , MyClass ) にのみ使用可 |
String? などのnull許容型には使えない |
✅ プリミティブ型(Int , Boolean など)には使えない |
Java互換の都合上 |
✅ クラスのメンバ変数・トップレベル変数にのみ使用可 | ローカル変数には使えない |
チェック方法
lateinit
変数が初期化済みかどうかを安全に確認するには:
if (::name.isInitialized) {
println("Initialized: $name")
} else {
println("Not initialized yet")
}
::name
は プロパティ参照(Property Reference) で、
.isInitialized
はlateinit
専用プロパティです。
未初期化のままアクセスすると…
lateinit var title: String
fun show() {
println(title) // ❌ UninitializedPropertyAccessException
}
lateinit
を初期化前にアクセスすると、
UninitializedPropertyAccessException
が発生します。
つまり「コンパイル時」ではなく「実行時」に例外になります。
Androidでの典型例
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
}
ここでは onCreate()
で binding
を初期化します。
このように「Activity生成時にはまだnull」「でも必ずonCreateで初期化される」
とわかっている場合、lateinit
が最適です。
テストコードでの利用
class UserServiceTest {
private lateinit var userService: UserService
@Before
fun setup() {
userService = UserService()
}
@Test
fun testLogin() {
assertTrue(userService.login("Anna"))
}
}
JUnitなどのテストでは、
@Before
メソッドで後からモックを代入するために
lateinit
がよく使われます。
lazy
との違い
比較項目 | lateinit |
lazy |
---|---|---|
修飾対象 | var |
val |
初期化タイミング | 明示的に代入するとき | 最初のアクセス時(自動) |
null許容型 | ❌ 非対応 | ✅ OK |
スレッドセーフ | ❌ デフォルト非対応 | ✅ デフォルトでスレッドセーフ |
主な用途 | DI・View・テスト | キャッシュ・重い初期化処理 |
例:
// lateinit
lateinit var db: Database
db = Database.connect()
// lazy
val db by lazy { Database.connect() }
使いどころまとめ
シーン | 例 | 推奨 |
---|---|---|
AndroidのViewBinding | lateinit var binding |
✅ |
DI / Service注入 | lateinit var repository |
✅ |
テストのMock | lateinit var mockService |
✅ |
プリミティブ型の遅延初期化 | lateinit var count: Int |
❌ 不可 |
関数ローカル変数 | lateinit var x |
❌ 不可 |
まとめ
-
lateinit
は「nullを使わずに後で初期化する」ための宣言 - 未初期化アクセスで例外が出るため、
::var.isInitialized
で安全確認可能 - Androidやテストコードなど、「明確に初期化タイミングが決まっている」場面で有効
- 自動初期化やスレッドセーフを求める場合は
lazy
を検討する