0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Kotlin】Kotlinの lateinit 完全解説

Posted at

はじめに

Kotlin は「Null安全」を重視する言語です。
しかし時には「あとで初期化したい」場面もあります。

例えば:

  • Androidの Activity 内で onCreate() で初期化する ViewViewModel
  • 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) で、
.isInitializedlateinit 専用プロパティです。


未初期化のままアクセスすると…

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 を検討する

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?