views:

2320

answers:

4

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.

+1  A: 

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()

Hemal Pandya
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.
Scott Markwell
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
Hemal Pandya
+2  A: 

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();
    }
}
TofuBeer
You can walk the stack as StackTraceElements - it is not necessary or recommended to literally parse the string printed by printStackTrace().
mP
see my update for that
TofuBeer
A: 

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).

Tom Hawtin - tackline
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.
Scott Markwell
A: 

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.

Peter Lawrey