Java 9 StackWalker.getCallerClass() Example

[Updated: Oct 21, 2017, Created: Oct 19, 2017]

StackWalker.getCallerClass() returns the caller's Class. Formerly we had to use Throwable.getStackTrace() or Thread.getStackTrace() methods to do the same, there we had to loop through and drop unwanted frames to find the immediate caller. JDK itself has internally been using these methods or sun.reflect.Reflection#getCallerClass() (which forwards call to a native method) to find the immediate caller in different cases. Reflection#getCallerClass() has been deprecated in Java 9 and will be removed in a future release.

Example

Using StackWalker.getCallerClass()

public class StackWalkerCallerExample1 {
    public static void main(String[] args) {
        TheCallerClass sc = new TheCallerClass();
        sc.doSomething();
    }

    public static final class TheCallerClass {
        public void doSomething() {
            TheCalleeClass theCalleeClass = new TheCalleeClass();
            theCalleeClass.work();
        }
    }

    public static final class TheCalleeClass {
        public void work() {
            StackWalker instance = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
            Class<?> callerClass = instance.getCallerClass();
            System.out.println(callerClass);
        }
    }
}

In above example we used StackWalker.Option.RETAIN_CLASS_REFERENCE option which is needed when we use StackWalker.getCallerClass() method (we also used it in our last example while using StackFrame.getDeclaringClass().

Output

class com.logicbig.example.StackWalkerCallerExample1$TheCallerClass

In our above example, the StackWalker.getCallerClass() method is equivalent to the following StackWalker.walk() usage:

  ....
    public static final class TheCalleeClass {
        public void work() {
            StackWalker instance = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
            Optional<? extends Class<?>> opt = instance.walk(
                    stream -> stream.map(StackFrame::getDeclaringClass)
                                    .skip(1)
                                    .findFirst());
            if (opt.isPresent()) {
                Class<?> aClass = opt.get();
                System.out.println(aClass);
            }
        }
    }
 .....

Let's use the old way of looping though StackTraceElements:

public class StackWalkerCallerExample2 {
    public static void main(String[] args) {
        TheCallerClass sc = new TheCallerClass();
        sc.doSomething();
    }

    public static final class TheCallerClass {
        public void doSomething() {
            TheCalleeClass theCalleeClass = new TheCalleeClass();
            theCalleeClass.work();
        }
    }

    public static final class TheCalleeClass {
        public void work() {
            for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
                System.out.println(ste.getClassName());
            }
        }
    }
}

Output

java.lang.Thread
com.logicbig.example.StackWalkerCallerExample2$TheCalleeClass
com.logicbig.example.StackWalkerCallerExample2$TheCallerClass
com.logicbig.example.StackWalkerCallerExample2

As seen in above output, our required caller class is in the third stack frame.

Using Reflection

When using Java reflection, StackWalker.getCallerClass() filters reflection stack traces, whereas the old way of using StackTraceElements shows all internal reflection calls. This includes the cases when using the methods such as Method.invoke() or Constructor.newInstance() or recently deprecated method Class.newInstance(). Let's see an example.

public class StackWalkerCallerExample3 {
    public static void main(String[] args) {
        TheCallerClass sc = new TheCallerClass();
        sc.doSomethingReflectively();
    }

    public static class TheCallerClass {
        public void doSomethingReflectively() {
            try {
                TheCalleeClass theCallerClass = TheCalleeClass.class
                        .getConstructor().newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static class TheCalleeClass {
        public TheCalleeClass() {
            System.out.println("-- using StackWalker --");
            StackWalker instance = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
            Class<?> callerClass = instance.getCallerClass();
            System.out.println(callerClass);

            System.out.println("-- using stack trace --");
            for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
                System.out.println(ste.getClassName());
            }
        }
    }
}

Output

-- using StackWalker --
class com.logicbig.example.StackWalkerCallerExample3$TheCallerClass
-- using stack trace --
java.lang.Thread
com.logicbig.example.StackWalkerCallerExample3$TheCalleeClass
jdk.internal.reflect.NativeConstructorAccessorImpl
jdk.internal.reflect.NativeConstructorAccessorImpl
jdk.internal.reflect.DelegatingConstructorAccessorImpl
java.lang.reflect.Constructor
com.logicbig.example.StackWalkerCallerExample3$TheCallerClass
com.logicbig.example.StackWalkerCallerExample3

Example Project

Dependencies and Technologies Used :

  • JDK 9
Java 9 StackWalker.getCallerClass Examples Select All Download
  • stack-walker-caller-examples
    • src
      • com
        • logicbig
          • example

See Also