Finding Caller object, method/class of an instance method in Java
You might think if we would ever need it? Well... you may never, but no harm in exploring whether we have any/some ways of finding this in Java or not. This question was asked by one of our visitors - Marco Servetto. Thanks Marco for bringing this question up.
For instance methods, by caller you might mean either the object instance of the caller/calling method or method/class name of the caller/calling method. But, for static methods the caller will never be an instance, but only the other two (method/class).
In case of a static method, we don't have the implicit object reference (this) passed to them as they are never called on instances (even if you call static methods on object references, the call is actually resolved at compile time only and is made on the declared type of the object reference). And hence for them the caller will never be an instance.
If we are interested in finding the object associated with the calling method inside the called method then the most obvious way is to pass the reference 'this' to the called method while making the call in the calling/caller method. I doubt if we have any other JVM implementation independent way of doing this. Better to understand it by code, which is pretty simple and straightforward.
public class FindingCallerDemo {
public static void main(String[] args) {
new CallerClass().callerMethod();
}
}
class CallerClass{
public void callerMethod(){
Object returnedObj = new CalledClass().calledMethod(this);
//assert returnedObj == this;
if (returnedObj == this)
System.out.println("Success!");
else
System.out.println("Failure!");
}
}
class CalledClass{
public Object calledMethod(Object obj){
return obj;
}
}
By caller if we mean the name of the caller method name OR the name of the class of the caller method then we can probably use the getStackTrace() method either on current thread instance or on a Throwable instance:
- Using getStackTrace on current thread (since Java 1.5) - 'StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace()' - we can use this to get the stack trace of the current thread in a StackTraceElemnt array where the first element of the array is the most recent method invocation sequence on the stack - provided the returned array is of non-zero length. StackTraceElement has methods like getClassName, getMethodName, etc., which one can use to find the caller class name or method name.
- Calling getStackTrace on a Throwable instance (since Java 1.4) - creating an instance of Throwable and then calling getStackTrace() method on it, will return an array of StackTraceElement, each of which will contain one stack frame each with the first element containing the most recent inovaction and the last having the least recent. You don't really need to throw the Throwable instance, only creating one would be good enough here.
StackTraceElement[] ste = new Throwable().getStackTrace();
for (int i = 0; i < ste.length; i++)
System.out.println("Class Name: " + ste[i].getClassName()+ ", Method Name: " + ste[i].getMethodName());
Potential Problem: In certain situations, few JVM implementations may either omit one or more stack frames from the stack trace or they might not have any stack frame info at all related to this Throwable instance as a JVM implementation is allowed to return a zero-length array by Java language specs.
Another possible issue is that compile-time inlining may show skewed results. Say if a method A internally calls another method B and B calls C. Let's suppose the method B has no other executable statement. Obviously this will be a good candidate for compile time inlining as the compiler might replace the call to method B inside method A definition with a call to method C. In such cases, the above two approaches will return the caller method of method C as method A and not as method B what the source code might suggest. Please note that this will require you to turn on the optimization of the compiler. Results may vary across compilers as different compilers may have different criteria for inlining - some might never do.
Other Possible Solutions: there can be some other possible solutions using a native method (like getCallerClass method) of some JVM implementation specific classes. We should avoid such a practice as we may otherwise sacrifice the portability of the code. Even worse, in case the implementation specific class is non-public then you might not use your code on the changed version of the same JVM implementation itself.
Liked the article? Subscribe to this blog for regular updates. Wanna follow it to tell the world that you enjoy GeekExplains? Please find the 'Followers' widget in the rightmost sidebar.
4 comments:
Thanks for the answer, I was aware of the stackTrace solution for recovering method name etc.
My purpose is to identify the this of the caller for implementing an (assertions) system for ownership type.
I hope I can reach this syntax:
@SomeDesideredCondition1 @SomeDesideredCondition2 @SomeDesideredCondition3
int foo(){
assert check();//only in some method under control.
some code
}
But to be able to reconstruct the stack trace with the this values passing it as a parameter, all methods have to follow this pattern:
@SomeDesideredCondition1 @SomeDesideredCondition2 @SomeDesideredCondition3
int foo(){
if(debug)check(this);
try{
some code
}
finally{
if(debug)check();
}
}
(where debug is a compile time known constant)
There are two "hacks" inside this code:
try finally to emulate pre/post condition.
debug var for conditional compilation, in fact if we use assert instead of if(debug), well, in case we are not debugging the compiler have still to generate a try finally, slowing down the code
up to 10X in the worst cases.
I know I can use AspectJ to enforce this pattern, but I'm trying to use pure java.
Please share with us in case you find a pure Java solution (portable one) to find the caller object without passing 'this'.
I assume 'check()' in your finally-block is not intentionally missing the 'this' argument - maybe a typo?
"in case we are not debugging the compiler have still to generate a try finally" - well I think try-finally block would anyway be generated irrespective of whether you are debugging or not as in your code 'if(debug)' test is only covering the call to 'check' method, isn't it? I agree that code for the 'check' call (or anything else you would do in the local if-block) would not be generated in case you turn 'debug' off.
Thanks for your participation. Looks like you are actually in a real situation where you need this OR are you just trying out to explore if it's possible or not? We don't encounter such a situation very frequently, so it will be good if you can give us a brief about it. Though, 'debug' gives an impression that the requirement is not a part of the actual app/tool. Keep visiting/posting!
"Though, 'debug' gives an impression that the requirement is not a part of the actual app/tool."
The tool i'm thinking about is for helping in debug :)
"well I think try-finally block would anyway be generated irrespective of whether you are debugging or not"
making some test i have seen the whole try{stm}finally{}
turned by the compile time java into stm if the finally block is empty. and became empty thanks to the static if.
Good point. Generating try-finally code for an empty 'finally' doesn't really make sense and as you have rightly said that a static (compile-time) if-condition will make the 'finally' block empty when 'debug is turned off. I appreciate your active participation. Thanks for your inputs!
Post a Comment