Static initializer not called on Activity creation

2019-08-12 16:02发布

I have the following code in my main Activity:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Fabric.with(this, new Crashlytics());
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        MyClass[] arr = MyClass.values();
        System.out.println(Arrays.deepToString(arr));

MyClass is as follow:

public class MyClass {

    public static MyClass A;
    public static MyClass B;
    public static MyClass C;

    static {
        System.out.println("Invoked");
        A = new MyClass("a");
        B = new MyClass("b");
        C = new MyClass("c");
    }

    private static Map<String, MyClass> values = null;
    private final String name;

    private MyClass(String name) {
        this.name = name;
        if (values == null)
            values = new HashMap<>();
        values.put(name, this);
    }

    public static MyClass[] values() {
        return values.values().toArray(new MyClass[values.size()]);
    }
}

My issue is that values() triggers a NullPointerException, as values is null, i.e. the static initialization block seems not to be invoked. Nevertheless, the console out "Invoked" appers, so the initialization block DOES get called.

What's wrong?

EDIT: this question is strictly related to this: Issue with static code in Android

1条回答
戒情不戒烟
2楼-- · 2019-08-12 16:18

It's because private static Map<String, MyClass> values = null; is after the static initializer: this assignment will be run after the static initializer block, so it is being assigned a non-null value, and then nulled out.

Move the values field before the initializer block.


BTW, I would decouple the creation of the instances from inserting them into the map, in order to remove the side effects when creating an instance of MyClass. You can also do it without a static initializer block:

public class MyClass {
    private static final Map<String, MyClass> values = new HashMap<>();

    public static final MyClass A = register(new MyClass("a"));
    public static final MyClass B = register(new MyClass("b"));
    public static final MyClass C = register(new MyClass("c"));

    private static MyClass register(MyClass instance) {
      values.put(instance.name, instance);
      return instance;
    }

    private MyClass(String name) {
      this.name = name;
    }
    // ...
}

Actually, I wouldn't even do this: I'd use an enum - but I guess there is something in your real code that stops you from doing that.

查看更多
登录 后发表回答