Help us understand the problem. What is going on with this article?

Kotlin : 'notNull delegate' vs 'lateinit'

More than 3 years have passed since last update.

はじめに

Kotlinには、プロパティを遅延して初期化する際に主に2つの方法があります。Delegation Propertyとlateinitです。
lateinitは、KotlinのM13から追加された機能で、Delegation Propertyよりも後に追加された機能となります。

この2つの機能の使い所は、似ていて。
constructorよりも後に、Nullを許可しない変数への代入を行いたい場合に使用されます。
この記事では、この2つの機能について何が違うのかと、使い所を検証していきます。

デコンパイルによる検証

Kotlinのコードが、JVM上でどのように扱われているのかデコンパイルして確認をします。
以下のような、それぞれの値の初期化を使用した変数を2つもったクラスを作成し、デコンパイルを行います。
※実行する部分は、省いています。

PreCompiled.kt
class Main {

    var delegatesValue: String by Delegates.notNull()
    lateinit var lateinitValue: String

    fun setValues() {
        delegatesValue = "Delegates.notNull"
        lateinitValue = "lateinit"
    }
}

こちらが、デコンパイルしたコードになります。
何個かポイントをあげて解説を行います。

まずDelegates.notNullはReadWritePropertyなため、裏ではReadWritePropertyとして宣言されます。そして、ReadWritePropertyとして宣言されたものはKPropertyという形でReflectionを使用してアクセスされます。
次に、lateinitを使用したものは、Javaの標準の宣言と同じく宣言されます。

Decompiled.java
public final class Main {
   @NotNull
   private final ReadWriteProperty delegatesValue$delegate;
   @NotNull
   public String lateinitValue;
   // $FF: synthetic field
   static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.mutableProperty1(new MutablePropertyReference1Impl(Reflection.getOrCreateKotlinClass(Main.class), "delegatesValue", "getDelegatesValue()Ljava/lang/String;"))};

   @NotNull
   public final String getDelegatesValue() {
      return (String)this.delegatesValue$delegate.getValue(this, $$delegatedProperties[0]);
   }

   public final void setDelegatesValue(@NotNull String var1) {
      Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
      this.delegatesValue$delegate.setValue(this, $$delegatedProperties[0], var1);
   }

   @NotNull
   public final String getLateinitValue() {
      String var10000 = this.lateinitValue;
      if(this.lateinitValue == null) {
         Intrinsics.throwUninitializedPropertyAccessException("lateinitValue");
      }

      return var10000;
   }

   public final void setLateinitValue(@NotNull String var1) {
      Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
      this.lateinitValue = var1;
   }

   public final void setValues() {
      this.setDelegatesValue("Delegates.notNull");
      this.lateinitValue = "lateinit";
   }

   public Main() {
      this.delegatesValue$delegate = Delegates.INSTANCE.notNull();
   }

}

この結果を見ると、Delegatesの方がReflectionを使用している分、重い処理をしています。

どちらを使えばいいのか

結局、知りたいことはDelegates.notNullとlateinitどちらを使うのがいいかということだと思います。
結論から言えば、大抵の場合はlateinitで事足りますし、lateinitを使ったほうがいいと思いました。
では、なぜDelegates.notNullが存在するのかというと、その答えはKotlinのフォーラムの以下の回答の通りです。

https://discuss.kotlinlang.org/t/notnull-delegate-vs-lateinit/1923

簡潔に訳すと、

  • notNullを使用したものは、各プロパティについてごく小さい追加のオブジェクトを定義します。ただし、数が多くなると無視できない差を生みます。
  • 外部からのJava Fieldへのインジェクション(DIなど)にnotNullを使用できません。
  • 値型(Int, Long, etc)には、lateinitを使用できません。

つまり、まとめると。

lateinit notNull
値型
オブジェクト型 △(ただし、DIなどは☓)

ということになります。
lateinitとnotNull、用法を守って使っていきましょう!

おまけ

エラーの違い

それぞれ、値が入っていない際にアクセスした際のエラーについて見てみます。

未初期化のnotNullプロパティに対して、アクセスした場合のエラー

java.lang.IllegalStateException: Property delegatesValue should be initialized before get.

未初期化のlateinitプロパティに対して、アクセスした場合のエラー

kotlin.UninitializedPropertyAccessException: lateinit property lateinitValue has not been initialized

ということで、notNullを使用した場合はjava.lang.IllegalStateExceptionに、lateinitを使用した場合はkotlin.UninitializedPropertyAccessExceptionとなります。

参考文献

Reyurnible
目黒近辺で、PMなどのお仕事をしています。副業では、エンジニアメインで仕事しています。 その他、ポートフォリオについてはWantedlyのプロフィールをご覧ください。 https://www.wantedly.com/secret_profiles/fdV5nsJR1gVVqGgcKkvrXeRtPEsmR8Of
recruitmp
結婚・カーライフ・進学の情報サイトや『スタディサプリ』などの学びを支援するサービスなど、ライフイベント領域に関わるサービスを提供するリクルートグループの中核企業
http://www.recruit-mp.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした