Is there a way in Java to detemine if a method is called in a static initializer (or not)?

Go To StackoverFlow.com

2

as in a way to check appropriate use of a static registry:

class AClass {
     static final IDType = IDregistry.registerId(...);
}

class IDRegistry {
     public static registerId(...)
     {
          if(isCalledInStaticInitializer()) {
               return(new IDType(...));
          }
          assert false : "NO NO - can't do this !!!";
     }
}
2012-04-05 23:18
by peterk
Could you expand upon your use case a little more? I am almost certain you really don't need to try something like that - Andrew T Finnell 2012-04-05 23:24
Really, really bad idea - but you should be able to do it with a bit stack walking and reflection. But if you don't know how to do that, it's probably a really good idea to not try that stuff. I'm sure if you describe what you're actually trying to do there'll be better ways - Voo 2012-04-05 23:39
simple - if the method is called outside of a static initializer it is inappropriate in terms of how the method is intended to be used. simply to test for this error case and flag the inappropriate use - peterk 2012-04-07 01:06


3

I don't think you should do this. But if you insist, this would get you started:

public static boolean isCalledInStaticInitializer()
{
    for (StackTraceElement ste : Thread.currentThread().getStackTrace())
    {
        if("<clinit>".equals(ste.getMethodName()))
        {
            return true;
        }
    }
    return false;
}

Source: In section 2.9 of the JVM Specification ("Special Methods"):

"A class or interface has at most one class or interface initialization method and is initialized (§5.5) by invoking that method. The initialization method of a class or interface has the special name <clinit>"

2012-04-05 23:50
by Mike Clark
Thread.currentThread().getStackTrace() is probably a better way to get hold of the call stack. Also, I think a ste.getClassName() should also be checked in the if clause before returning true - shams 2012-04-06 04:27
@shams I changed it to use Thread.currentThread().getStackTrace() because it seems more elegant and more efficient. I do not think he wants to test ste.getClassName() because he does not know ahead of time which class' static initializers he is guarding against - Mike Clark 2012-04-06 20:45
I did "check" this - Thanks it does answer if "" is always as defined by Java VM the method name that is, and is only called to do the static initialization. But just seems so horribly inelegant :) I didn't care about the class being initialized simply whether it was called in the static initializer context or not - peterk 2012-04-07 01:19
@peterk: It is inelegant, but the special name <clinit> is well-defined by the JVM specification, and so can be relied upon - Mike Clark 2012-04-07 03:24
@MikeClark thanks - nice to kno - peterk 2012-04-07 12:21


1

Your intention is unnecessary (no one ever does it - I've never seen it etc).

What you want is simply this:

static final IDType id = new IDType(...);

If you need to register the ID somewhere, put that code in the constructor of IDType if the IDType is immutable, or if it's mutable (unlikely) one option would be to use a factory method in IDType to create-and-register:

public class IDType() {
    ...
    public static IDType createAndRegister(...) {
        IDType idType = new IDType(...);
        SomeClass.register(idType);
        return idType;
    }
}
2012-04-05 23:31
by Bohemian
Making this accessible to other objects before the constructor is complete is a common cause of concurrency bugs. I don't think this is a good suggestion - erickson 2012-04-05 23:36
@erickson Fair point, but it depends if it's mutable or not. Immutables, once initialized, can be safely published - Bohemian 2012-04-05 23:38
An object is not completely initialized until its constructor is complete. (See JLS 17.5.) Immutability is not sufficient to guarantee thread safety. Your second code example should always be used; registration from the constructor as suggested by the first sample and remarks is not safe - erickson 2012-04-06 00:11
it would be immutable. But the issue is if anyone is creating transient instances of this object it is not the intended use, so I wanted to test if where it is created is being called in a static initializer or not to flag this inappropriate use - peterk 2012-04-07 01:11
I would assume if there is a obj = new Obj() then "obj" would not even be assigned to until the new Obj() is fully initialized so there would be no way for anything to access it until it is complete - peterk 2012-04-07 12:24


0

Static initialization is done on a per-class basis. So the question the question you might need to ask yourself is "which class's static initialization?". From your use case it looks like the IDRegistry's static initialization is run before any of the classes that register themselves. You might take a different approach and look at something else that you want to enforce about what can be added to the registry.

BTW you detect within a single class's initialization process, by using a final variable that you declare at the very beginning on the class, and then set in a static block at the very end of the class. Since static initialization is done in source order, any code executed as part of the classes static initialization, will see the java default value for the type (i.e. false, 0, null), and any code executed after static initialization will see the final set value.

class AClass {
    static final boolean staticInitDone;

    // Any static initialization done here will see
    // staticInitDone as false, e.g. the
    // Y constructor below would see false.
    static X = new Y();


    static {
        staticInitDone = true;
    }
}

But you'll note this doesn't adapt to the cross-class coordination that you want.

2012-04-06 03:46
by ɲeuroburɳ
actualy simply to test whether it is being called in any static initialization - peterk 2012-04-07 01:08
Ads