16

What is the most elegant way to get the currently executing method as a Method object ?

My first obvious way to do it would be to use a static method in a helper class, which would load the current thread stack, get the right stack trace element, and construct the Method element from its information.

Is there a more elegant way to achieve this?

7
  • 1
    The down side of this is handling overloaded methods is complicated. My suggestion would be to find another way to do what you want to do without the Method object. i.e. Why do you need it? Commented Feb 28, 2011 at 9:12
  • It might be worth taking a look through the source code of some of the logging APIs that need to do this. Some of these projects have been around for a long time, and should have some well worn solutions to this problem Commented Feb 28, 2011 at 9:18
  • @Peter : You're right, but I'm annotating the methods I need to find, so no worry about overloading. Actually finding the Method object dynamically allows me to avoid even worse solutions to my problem.
    – glmxndr
    Commented Feb 28, 2011 at 9:26
  • @magicduncan: Interestingly, all of the logging frameworks I can think of off the top of my head, don't do this out of the box. Log4J can do this (see PatternLayout's %L or %M options) but warn that it's very slow, because IIRC they're just calling Thread.currentThread().getStackTrace() as well. Commented Feb 28, 2011 at 9:28
  • In Oracle's JVM you can call sun.reflect.Reflection.getCallerClass(int) This can more efficient, but platform dependant and it doesn't give you the method. Commented Feb 28, 2011 at 9:33

4 Answers 4

21

This topic is covered in deeper depth in this SO Question.

I need to do the same thing and I found the best solution to be the one provided by @alexsmail.

It seems a little bit hacky but the general idea is that you declare a local class in the method and then take advantage of class.getEnclosingMethod();

Code slightly modified from @alexsmail's solution:

public class SomeClass {
   public void foo(){
      class Local {};
      Method m = Local.class.getEnclosingMethod();
   }
 }
1
  • 10
    Another streamlined version might be this: new Object (){}.getClass ().getEnclosingMethod () Commented Jun 10, 2015 at 18:08
2

Out of the box, I'm not aware of a better way to do this.

One thing to consider perhaps would be aspects - you could weave an aspect into the code that fired around all method invocations and pushed the current Method object to a ThreadLocal (based on the reflective information available from the joinpoint).

This would be probably prohibitively expensive if it really fired on all methods, but depending on what you're doing with the results, you may be able to constrain the capturing down to certain packages/classes, which would help. You may be able to defer the actual lookup of the Method too until such time as it's used, and instead store the name of the method and arguments etc.

I have my doubts whether there's going to be a particularly cheap way to achieve this, though.

0
0

I find that this works well:

package com.paintedintel.util;

public class Log{

    public static void out(final String output) {
        System.out.println(output);
    }
    
    public static void debug(String output) {
        System.err.println(Log.currentLocation() + " " + output);
    }
    
    public static String currentLocation() {
        StackTraceElement classMethod = new Throwable() 
                .getStackTrace()[2];
        String   currMethod = classMethod.getMethodName(); 
        String   fullClass  = classMethod.getClassName();
        String[] smplClass  = fullClass.split("\\.");
        return smplClass[smplClass.length - 1] + "." + currMethod;
    }   
}

You can call it as

Log.debug("my message " + myParameters);

I have it printing out the simpleClassName() rather than the package class name which you can't get at directly which is the

split("\\."); //yes you need to escape the "."

Other than that, it can track where you're debug output is coming from automatically. Or you could just use it to get the method and class names.

If you combine the debug and currentLocation methods you'll have to use a lower index to get the line you want....

        StackTraceElement classMethod = new Throwable() 
                .getStackTrace()[2]; // use a different index [x] w/exr methods
2
  • You don't need to create a throwable, you can get the stack trace from the current thread: Thread.currentThread().getStackTrace().
    – Lengors
    Commented May 5, 2023 at 22:22
  • Also creating a new exception is very heavy on performance, if you look at the constructor of Throwable you'll see why. Commented Jul 18, 2023 at 10:53
0

If you are using Java 9+ then you can use StackWalker:

public void foo() {
    StackWalker walker = StackWalker.getInstance();
    Optional<StackTraceElement> stackTraceElement = walker.walk(frames -> frames
            .findFirst()
            .map(StackWalker.StackFrame::toStackTraceElement));
    
    if (stackTraceElement.isPresent()) {
        // from here you can construct the method being called
        // the same way as you would with the other methods mentioned
    }
}

The advantage of this method is that each StackTraceElement is fetched lazily so you don't actually construct the full stack trace before checking the first method.

Not the answer you're looking for? Browse other questions tagged or ask your own question.