理系学生日記

おまえはいつまで学生気分なのか

static にまつわる実行順

static というとクラス変数、クラス関数につける修飾子というイメージがありますが、Java には static initializer と呼ばれるブロックを static キーワードで記述ができます。
下のはクソ簡単な例ですけど、Map にエラーコードとメッセージのマッピングを入れておきたいとかいうときに使ったりするんじゃないでしょうか。

import java.util.*;

public class Test {
    private static Map<String, String> testMap = new HashMap<String, String>();
    static {
        testMap.put( "kiriri", "mode" );
    }

    public static void main( String[] args ) {
        System.out.println( testMap.get("kiriri") );
    }
}

もちろんこのコードは問題なくコンパイルできるわけですが、普段何気なく使っていたこの static initializer について突如疑問に思ったのは以下の点。

  • ここでは testMap の初期化が行われた後で static initializer 内のコードが実行されている。これは static 変数の初期化が static initializer の実行より常に先に来ることを意味するのか。

というわけで試してみたんですが、結論から言うと「static 変数の初期化と static initializer は書いた順番に従って実行される」みたいです。
というのも、以下のコードはコンパイルエラーになるんだ。

import java.util.*;

public class Test {
    static {
        testMap.put( "kiriri", "mode" );
    }
    private static Map<String, String> testMap = new HashMap<String, String>();

    public static void main( String[] args ) {
        System.out.println( testMap.get("kiriri") );
    }
}
kiririmode-mb:test% javac Test.java
Test.java:6: illegal forward reference
        testMap.put( "kiriri", "mode" );
        ^
1 error

まぁこれは単にコンパイルエラーなわけで、実行順を示せたとは言い難い。というわけでもっとシンプルな例。

public class Test {
    private static int T = 5;
    static { T = 10; }

    public static void main( String[] args ) {
        System.out.println( T ); // prints "10"
    }
}
public class Test {
    static { T = 10; }
    private static int T = 5;

    public static void main( String[] args ) {
        System.out.println( T ); // prints "5"
    }
}

そういうことです。



じゃぁこれは。

import java.util.*;

public class Test {
    private static Map<String, String> testMap = new HashMap<String, String>();
    static {
        testMap.put( "kiriri", "mode" );
    }
    static {
        testMap.put( "kiriri", "kiriri" );
    }

    public static void main( String[] args ) {
        System.out.println( testMap.get("kiriri") );
    }
}

これは "kiriri" を出力する。