Software By JeffMain Page | About | Help | FAQ | Special pages | Log in
The Free Encyclopedia
Printable version | Disclaimers

Dynamically Retrieve Method Name

(Redirected from Dynamically Retreive Method Name)

When writing a Java class, it happens that you may need to have the method name. Most typically this is needed for something like logging an error or writing a diagnostic message.

By far, the most common pattern is to use a string literal (or even a variable) with the method's name in readable text. This is easy, and just plain works.

But what if you want to find the method name somewhere else? What if you want to survive a refactoring where your method is copied or moved to a different class or gets renamed?

Table of contents

1 JDK 1.5

Get Current Method

The following super-simple example Utilities class has a method that can be called by any other method to get the current method. To restate that a little...a method can call the getCurrentMethodName() created below, and receive its own name.

public class Utilities {
   public static String getCurrentMethodName() {
       ByteArrayOutputStream 
           byteArrayOutputStream = new ByteArrayOutputStream();
       PrintWriter 
           printWriter = new PrintWriter(byteArrayOutputStream);
       (new Throwable()).printStackTrace(printWriter);
       printWriter.flush();
       String stackTrace = byteArrayOutputStream.toString();
       printWriter.close();

       StringTokenizer 
           stringTokenizer = new StringTokenizer(stackTrace, "\n");

       // Line 1 -- java.lang.Throwable
       stringTokenizer.nextToken();

       // Line 2 -- "at thisClass.thisMethod(file.java:line)"
       stringTokenizer.nextToken();

       // Line 3 -- "at callingClass.callingMethod(file.java:line)"
       String methodName = stringTokenizer.nextToken();
       stringTokenizer = new StringTokenizer(methodName.trim(), " (");
       stringTokenizer.nextToken();
       methodName = stringTokenizer.nextToken();

       // Return callingClass.callingMethod
       return methodName;
   }
}

The execution of this couldn't be more simple:

public class UtilitiesTest {
    public static void main(String[] args){
        System.out.println("Method name: " + Utilities.getCurrentMethodName());
    }
}

This will trivially print the method's name to the console as Method name: UtilitiesTest.main. Of course, we'd rather do something more important.

Get Calling Method

Another, related problem, is how to get the name of the method that called the currently executing method. Something invoked the method we're concerned about, and we don't really have a Java helper for that.

public class Utilities {
   public static String getCallingMethodName() {
       ByteArrayOutputStream 
           byteArrayOutputStream = new ByteArrayOutputStream();
       PrintWriter 
           printWriter = new PrintWriter(byteArrayOutputStream);
       (new Throwable()).printStackTrace(printWriter);
       printWriter.flush();
       String stackTrace = byteArrayOutputStream.toString();
       printWriter.close();

       StringTokenizer 
           stringTokenizer = new StringTokenizer(stackTrace, "\n");

       // Line 1 -- java.lang.Throwable
       stringTokenizer.nextToken();

       // Line 2 -- "at thisClass.thisMethod(file.java:line)"
       stringTokenizer.nextToken();

       // Line 3 -- "at callingClass.callingMethod(file.java:line)"
       stringTokenizer.nextToken();

       // Line 4 -- "at originalClass.callingMethod(file.java:line)"
       String methodName = stringTokenizer.nextToken();
       stringTokenizer = new StringTokenizer(methodName.trim(), " (");
       stringTokenizer.nextToken();
       methodName = stringTokenizer.nextToken();

       // Return originalClass.callingMethod
       return methodName;
   }
}

Why would you want to know who called you? Perhaps your method was expecting something to have been prepared, and you want to know where to point the finger. Maybe you're abstracting a logging or error class, and you want to know who caused your action. In a logging class, for example, you don't want the name of the logging method, but the method that called the logging method.

public class UtilityTest {
    public static void main(String[] args){
        System.out.println(log("hello"));
    }

    public static String log(String message){
        return Utilities.getCallingMethodName() + " says " + message;
    }
}

This trivial example should display UtilityTest.main says hello in the console. Note the call to getCallingMethodName() didn't return UtilityTest.log as the method we were seeking, because we wanted the method that called log(), which is main().

Note this even works if that log() is in a different class, and, of course, has a different name. In that case, the Utility methods could even be part of the same class.

Common code

Of course, the above samples are drawn out to show what we expect in the stack trace. Additionally, if you're going to implement both of the above methods, we can see they have much common code that can be reduced for maintenance and readablity.

public class Utilities {
    public static String getCurrentMethodName() {
        return getMethodName(0);
    }

    public static String getCallingMethodName() {
        return getMethodName(1);
    }

    private static String getMethodName(int stackLevel) {
        ByteArrayOutputStream 
            byteArrayOutputStream = new ByteArrayOutputStream();
        PrintWriter printWriter = new PrintWriter(byteArrayOutputStream);
        (new Throwable()).printStackTrace(printWriter);
        printWriter.flush();
        String stackTrace = byteArrayOutputStream.toString();
        printWriter.close();

        StringTokenizer stringTokenizer = new StringTokenizer(stackTrace, "\n");
        String methodName = "";
        for (int i = 0; i < stackLevel + 4; i++) {
            methodName = stringTokenizer.nextToken();
        }

        stringTokenizer = new StringTokenizer(methodName.trim(), " (");
        stringTokenizer.nextToken();
        methodName = stringTokenizer.nextToken();

        return methodName;
    }
}

Now we have just one terse method responsible for parsing the stack trace, giving us just one place to improve the code with cleaner parsing. You may prefer something to StringTokenizer, or you may not want the fully-qualified class name; the package will be part of the name returned. You might want the filename (inner classes are not in their own .java file) and line number, which are trimmed out at the end, there.

JDK 1.5

As with many other things, the new JDK 1.5 provides us with enhancements that make this easier. The previous method took some tome to execute, and generated an Exception (surely you noticed that new Throwable() got us the stack trace. This is a potentially expensive operation that could impact performace. Certainly, the benefit during debugging or concise error trapping during execution can outweigh the impact on performance, but it might be a bit too much just to make a running log of things happening. While we can't do much different in JDK 1.4, we can as we move to JDK 1.5.

Thread

The Thread class has been given a new method getStackTrace() that can pull the same information we got from our Throwable before. This is just information that's hanging around already, and doesn't have the impact of generating exceptions.

The following snippet can be used in a method to get its names, both class and method. Of course, getClass().getName() will return a class' name, but that only works in instance methods; that is, you can't do that in a static method.

StackTraceElement[] stackTraceElement = Thread.currentThread().getStackTrace();
String className = stackTraceElement[2].getClassName();
String methodName = stackTraceElement[2].getMethodName();

Or shortened, that could be one of the following.

// Get the class.method
StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[2];
String name = stackTraceElement.getClassName() 
    + "." 
    + stackTraceElement.getMethodName();

// If you only care about the method
Thread.currentThread().getStackTrace()[2].getMethodName();

If you wanted to move this to your utility class, as above, just bump up the element you're looking for. Here's the same Utilities class as above, written with the improved methods provided by JDK 1.5.

public class Utilities {
    public static String getCurrentMethod() {
        return getCurrentMethodNameFromThread(0);
    }

    public static String getCallingMethodName() {
        return getCurrentMethodNameFromThread(1);
    }

    private static String getCurrentMethodNameFromThread(int stackLevel) {
        /*
         * 0 - dumpThreads
         * 1 - getStackTrace
         * 2 - thisMethod => getCurrentMethodNameFromThread
         * 3 - callingMethod => method calling thisMethod 
         * 4 - method calling callingMethod
         */
        StackTraceElement 
            stackTraceElement = 
                Thread.currentThread().getStackTrace()[4 + stackLevel];

        String className = stackTraceElement.getClassName();
        String methodName = stackTraceElement.getMethodName();

        return className + "." + methodName;
    }
}

If you desire the file name and line number, the StackTraceElement contains methods for retrieving them as well.

Retrieved from "http://www.swbyjeff.com/index.php/Dynamically_Retrieve_Method_Name"

This page has been accessed 21573 times. This page was last modified 17:00, 23 Sep 2005.


Find
Browse
Main Page
Community portal
Current events
Recent changes
Random page
Help
Edit
Edit this page
Editing help
This page
Discuss this page
Post a comment
Printable version
Context
Page history
What links here
Related changes
My pages
Create an account or log in
Special pages
New pages
Image list
Statistics
Bug reports
More...