1. 概要

このクイックチュートリアルでは、コンパニオンオブジェクトを使用してクラスを静的に初期化する方法を説明します。

まず、Javaでのstatic初期化について簡単に説明します。 次に、Kotlinでも同じことを実現します。 その過程で、Kotlinソリューションのバイトコード表現を確認します。

2. 静的初期化

Javaでは、クラスの static コンポーネントを初期化するために、 staticinitializerblocksを使用できます。

static {
    // put static initializers here
}

Kotlinにはstaticメンバーとstatic初期化子はありません。少なくとも、Javaと同様です。 ただし、コンパニオンオブジェクトを使用して、Kotlinで同じことを実現できます。

class Static {
    
    companion object {
        lateinit var answer: String
        
        init {
            answer = "42"
            println("Initialized")
        }
    }
}

クラスのコンパニオンオブジェクト内でinitブロックを宣言するだけです。このようにして、Javaのstaticブロックと同じ動作を取得します。

簡単に言うと、JVMが包含クラスを初期化するときに、コンパニオンオブジェクト内のinitブロックを実行します。

3. バイトコード表現

生成されたバイトコードを確認するには、まず、kotlincを使用してKotlinクラスをコンパイルする必要があります。

$ kotlinc Static.kt

それでは、javapツールを使用してバイトコードを見てみましょう。

$ javap -c -p -v Static 
Compiled from "Static.kt"
public final class com.baeldung.staticinit.Static {
  public static java.lang.String answer;
  
  // truncated
}

上に示したように、 companion object の変数は、囲んでいるクラス内のstaticフィールドとして宣言されています。 さらに、コンパニオン オブジェクト自体は、同じ包含クラス内のstatic内部クラスです。

InnerClasses:
  public static final #15= #37 of #2; // Companion=class Static$Companion of class Static

コンパニオンオブジェクトはシングルトンです。これは、コンパイラーがそのインスタンスを1つ作成するためです。

public final class com.baeldung.staticinit.Static {

  public static final Static$Companion Companion;
  // truncated 
}

そして最後に、initブロック自体が静的初期化ブロックとして内部でコンパイルされます

static {};
    Code:
       0: new           #37     // class Static$Companion
       3: dup
       4: aconst_null
       5: invokespecial #40     // Method Static$Companion."<init>":(LDefaultConstructorMarker;)V
       8: putstatic     #42     // Field Companion:LStatic$Companion;
      11: ldc           #44     // String 42
      13: putstatic     #20     // Field answer:LString;
      16: ldc           #46     // String Initialized
      18: astore_0
      19: iconst_0
      20: istore_1
      21: getstatic     #52     // Field System.out:LPrintStream;
      24: aload_0
      25: invokevirtual #58     // Method PrintStream.println:(LObject;)V
      28: return

init ブロックでロジックを実行する前に、JVMは companion クラス(インデックス0〜5)のインスタンスを作成し、それをstaticメンバーとして格納します。囲んでいるクラス内(インデックス8)。 その後、JVMは初期化ロジックを実行します。

つまり、言語レベルでの違いにもかかわらず、staticイニシャライザブロックとcompanion objectイニシャライザはバイトコードレベルで同じです。

4. 結論

この短いチュートリアルでは、最初に、Java staticイニシャライザーがどのように見えるかについて簡単に復習しました。 次に、コンパニオンオブジェクトおよびinit ブロックを使用して、Kotlinで同じことを実現できることを確認しました。 最後に、生成されたバイトコードを見ると、staticブロックとinitブロックがKotlinとJavaの両方で同じ内部で表されていることがわかりました。

いつものように、すべての例はGitHubから入手できます。