views:

562

answers:

6

I have a Set class (This is J2ME, so I have limited access to the standard API; just to explain my apparent wheel-reinvention). I am using my set class to create constant sets of things in classes and subclasses. It sort of looks like this...

class ParentClass
{
    protected final static Set THE_SET = new Set() {{
        add("one");
        add("two");
        add("three");
    }};
}


class SubClass extends ParentClass
{
    protected final static Set THE_SET = new Set() {{
        add("four");
        add("five");
        add("six");
        union(ParentClass.THE_SET); /* [1] */
    }};
}

All looks fine, except the line at [1] causes a null pointer exception. Presumably this means that the static initialiser in the subclass is being run before that of the parent class. This surprised me because I'd have thought it would run the static blocks in any new imports first, before running any in the instatiated subclass.

Am I right in this assumption? Is there any way to control or work around this behaviour?

Update:

Things are even stranger. I tried this instead (Note the 'new ParentClass()' line):

class ParentClass
{
    public ParentClass()
    {
        System.out.println(THE_SET);
    }

    protected final static Set THE_SET = new Set() {{
        add("one");
        add("two");
        add("three");
    }};
}


class SubClass extends ParentClass
{
    protected final static Set THE_SET = new Set() {{
        System.out.println("a");
        new ParentClass();
        System.out.println("b");
        add("four");
        System.out.println("c");
        add("five");
        System.out.println("d");
        add("six");
        System.out.println("e");
        union(ParentClass.THE_SET); /* [1] */
        System.out.println("f");
    }};
}

And the output is strange:

a
["one", "two", "three"]
b
c
d
e
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.NullPointerException

So ParentClass is initialised, but the subclass doesn't have access to it in its static initializer.

+3  A: 

There is no guarantee for static initializer order among classes. Within a class, they run in the order of the source code.

If you think about it, therre really couldn't be an order among classes, because you don't control when the classes are loaded either; you might dynamically load a class, or the JVM might optimize the load order.

Charlie Martin
If class A extends class B, wouldn't class A have to be fully loaded before class B can be loaded?
Mr. Shiny and New
I'd have thought it would be the other way around. Surely A depends on B being there, so B should be initialised first.
izb
But in order to get (or know about B) it has to go through the code in A first. Meaning the A static block code will happen before the code in B, I would think.
jschoen
Perhaps. I don't know what a class file looks like.. I had imagined that the compiler would list the imports first.
izb
You-all are hitting the problems. First, actually, no, the class declaration at compile time gives enough information to know the methods etc. All the JVM has to do is make sure that the class is loaded when the derived class code calls the base class code. It could conceivably be built so that a static block would force dependent classes' static blocks to be initialized, but it isn't done that way in reality.
Charlie Martin
+2  A: 

Even if you didn't have the extends ParentClass in there, using ParentClass should cause it to become initialised.

Where things become tricky is when you have cycles. With a cycle it is possible to access a class before it has been fully initialised. As Java is a multithreaded system, you can also have problems with deadlocks and races.

May be your Java ME implementation is buggy (not unheard of). Part your full ParentClass references your ChildClass. Or perhaps there is some other application/library bug.

On a related note, if you don't -target 1.4 or later, inner classes' outer this is not initialised as soon as you might expect. As presented your code uses (technically) inner classes in a static context, so that shouldn't be a problem.

Also it's interesting to point out that static initialisation is slightly confused in situations like this, because you actually have four classes there.

Tom Hawtin - tackline
+6  A: 

Is this what you are trying to accomplish? Or do you need a local implementation of the Set interface?

class ParentClass
{
    protected final static Set THE_SET;

    static {
        THE_SET = new HashSet();
        THE_SET.add("one");
        THE_SET.add("two");
        THE_SET.add("three");
    }
}


class SubClass extends ParentClass
{
    protected final static Set THE_SECOND_SET;

    static {
        THE_SECOND_SET = new HashSet();
        THE_SECOND_SET.add("four");
        THE_SECOND_SET.add("five");
        THE_SECOND_SET.add("six");
        union(ParentClass.THE_SET); /* [1] */
    }
}
Elijah
Yep.. that does the same thing.
izb
I think Elijah put it right. This is not a problem of initilization order, but rather a sort of name clash.
boutta
In my real code, I actually have 3 classes where A<-B<-C. The exception was in B. Interestingly giving each class its own set name moves the exception to C but doesn't solve the problem. This simply implies that the order really is unpredictable.
izb
Oh, wait. The real code was like this in 'C'... it started with a add(SOME_CONST)which was after the set in code, so it was SOME_CONST that was null. Seems the naming solution is the answer I was looking for :)
izb
A: 

Given the output line of ["one", "two", "three"], it is basically impossible that ParentClass.THE_SET has never been initialized.

Of course, it is possible that there isn't only one classloader involved, but it would certainly be helpful to see the method and line number where the null pointer happens.

Yishai
J2ME doesn't allow custom classloaders.. it's rather primitive. Also, the lack of 'f' on the output implies that the error occurred on the union() line that precedes it.
izb
+2  A: 

Simply stop abusing the concept of anonymous classes for instance initialization (the so-called "double brace idiom").

Michael Borgwardt
You know I always thought this was just handy syntax.. I had no idea it was actually an anonymous class.
izb
That's the biggest reason why I hate people calling it an "idiom" and giving it a name - it makes it sound like a separate feature of the language and obscures what actually happens.
Michael Borgwardt
A: 

I think something similar was written in Java puzzles book or google's YouTube video about java tricks.