83
63

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Android】lateinit と by lazy の使い分け【Kotlin】

Last updated at Posted at 2020-09-19

プロパティの初期化を遅らせる方法

Android 開発ではプロパティの初期化を遅らせたい場面がけっこうあります。

例えば、以下のようなケース。

.kotlin
class MainActivity : AppCompatActivity() {
    // この時点では view が生成されていないので view が取れない。
    // とりあえず null を代入...
    private var textView: TextView? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ここで初めて view が取れる。
        textView = findViewById(R.id.text_view)
    }

    //...
}

Kotlin でプロパティの初期化を遅らせる方法として lateinitby lazy がよく使われると思うのですが、機能が似ているのでどのように使い分ければいいのか悩むときがあります。

lateinit の特徴

lateinit には以下のような特徴があります。

  • int などのプリミティブ型は指定できない
  • var で宣言する必要がある
  • 必ず non-null となる

先ほどの例を lateinit で書くと次のようになります。

.kotlin
class MainActivity : AppCompatActivity() {
    private lateinit var textView: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ここで初めて初期化できる。
        textView = findViewById(R.id.text_view)
    }

    //...
}

最初の例ではプロパティ textView を null で初期化する必要がありましたが、lateinit を使うことで nun-null として扱うことができるようになりました。

ただし、var で宣言する必要があるため、別の値が再代入されてしまう可能性があるのが注意点です。

by lazy

次に by lazy の特徴を見てみます。

  • 型に制限なし
  • val で宣言する必要がある
  • nullable、non-null どちらも可能
  • 対象のプロパティが初めて呼び出されたときに初期化される
  • 初期化されたら次回以降は必ず同じ値を返す
.kotlin
class MainActivity : AppCompatActivity() {
    // nullable でも OK
    private val userData: User? by lazy { fetchUserData() }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 初めて呼び出されるタイミングで初期化される。
        userData?.let { showUserDate(it) }
    }

    //...
}

by lazy は一度初期化されると値はキャッシュされ、以降必ず同じ値を返します。そのため、ビューを by lazy で初期化してしまうと、ビューに変更を加えようとしても反映してくれません。ビューを扱う場合は lateinit を使う方がいいでしょう。

逆に by lazy は nullable なプロパティも扱うことができるため、null が入る可能性がある場合は by lazy を使うべきかと思います。

どのように使い分けるべきか

どちらでも OK な場合や、「プリミティブ型だけど、後から変更を加えたい」といったどちらにも当てはまらない場合もあるかと思いますので、それぞれの状況に合わせて使い分けてみてください。

※ 「プリミティブ型だけど、後から変更を加えたい」 場合には by Delegates.notNull が選択肢になるかなと思います。

lateinit が良さげな場面

  • 初期化するタイミングを指定したい場合
  • 型が View の場合
  • 後から変更を加えたい場合

by lazy が良さげな場面

  • 初期化するタイミングにこだわりがない場合
  • 型がプリミティブの場合
  • 後から変更されたくない場合
  • nullable な場合
  • 必ず使われるとは限らない場合(使われない場合は初期化されない)

ちなみに

これは個人的な感想になりますが、どちらでも OK な場合は可読性が高そうな by lazy を使うようにしています。

by lazy はプロパティの宣言と初期化処理を一体化させたハッピーセット🍔 なので、わざわざ初期化処理を確認しに行く必要がありません。

本来 onCreate() などに記述する初期化処理が減ることで、その他の重要なロジックに集中することができるのも嬉しいポイントです。

83
63
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
83
63

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?