Monday, April 11, 2011

getClass() and static methods: What is the best practice?

I am looking to provide some convenience through reflection to programmers who will be using my code. To achieve my desired result I would like to get the class object for classes that extend my code. Even though they can't override the static method I wish to access the information from, it would be currently helpful. I may end up simply refactoring the design a bit to avoid this situation, but wanted to toss this question out to the community.

The only way to achieve this that I am aware of is to take the current thread, navigate the stack trace, and extract the class name, then convert that into an actual instance of the class.

Any other suggestions?

Side note: the above approach is also the only way that I'm aware of getting the calling method.

From stackoverflow
  • When in the static method, what is the guarantee that some (any) class that has been extended from yours is on the stack?

    If you think about this a little, I suspect you will realize what you are looking for does not exist.

    /EDIT 1/ I think what you need is

    abstract class B {
        <T extends B> B create(Class<T> clazz) {
        ....
    }
    

    Hmmm. What if create is protected method? Then you could be pretty sure the caller is a B, you can even require that, throwing UnsupportedOperation or IllegalState if the caller is not assignable to B.

    You can use Thread.currentThread().getStackTrace()

    Scott Markwell : This is what I'm really afraid of. For what I'm doing, static methods that return instances of the classes they represent, backed by reflection based auto-wiring. I think I'll go the refactor route, and have a central factory class that accepts a Class as well as the original request.
    Hemal Pandya : Oh, but the static method only "represent" the class in which they have been declared. You *will* need a Class instance. See my example in /EDIT 1/ above
  • Walking the stack is not necessarily safe to do unfortunately:

    From the JDK 6 Javadoc for getStackTrace:

    Some virtual machines may, under some circumstances, omit one or more stack frames from the stack trace. In the extreme case, a virtual machine that has no stack trace information concerning this throwable is permitted to return a zero-length array from this method. Generally speaking, the array returned by this method will contain one element for every frame that would be printed by printStackTrace.

    You could, perhaps, use the debugger api to do it. I believe that the debug stuff is able to use HotSpot at full speed (I could be wrong) so you would not see a massive speed hit for doing it. You might start by looking at: http://java.sun.com/javase/6/docs/technotes/guides/jpda/trace.html

    Also, building on Unanswereds answer...

    class Base
    {
        // instance method helper
        public final int foo()
        {
            return (foo(this.getClass()));
        }
    
        // the real helper, with access to the class
        private static int foo(final Class clazz)
        {
            return (0);
        }
    }
    
    class Child
        extends Base
    {
        public void bar()
        {
            final int x;
    
            x = foo();
        }
    }
    
    mP : You can walk the stack as StackTraceElements - it is not necessary or recommended to literally parse the string printed by printStackTrace().
    TofuBeer : see my update for that
  • I suggest avoiding the quagmire of reflection. You appear to be doing some kind of straightforward programming. Use a straightforward implementation (there's not enough information to make a reasonable guess what that might be).

    Scott Markwell : I'm working on a Java ORM as a little side project. Part of the goals are a) little non-java configuration (no xml schema def, migrations in java) b) no code generation (at compile time) To achieve these goals in an abstract way, reflection provides a lot of syntax sugar.
  • Creating a Throwable is very expensive, so don't use this if your method is called often (.e.g. more than once a second) A slight better approach is Thread.currentThread().getStackTrace().

    Another approach if you are using Sun's JVM is Reflection.getCallerClass(n). This much faster but not portable to other JVMs. If you concerned about this you could have another method which calls this if its available and uses the stack trace if not.

    All this assumes your static method is NOT public. If it is all bets are off as your method can be called by any class, not just one extending yours.

0 comments:

Post a Comment