2

Here's what I'm trying to do:

In my code, I have sets of classes that create objects of other classes. What I am trying to do is, in the constructor of the class that's constructor gets called, find the class which called the constructor in the first place.

For example:

Main.java:

public class Main {

    public static void main(String[] args) {
        Test t = new Test();
    }

}

Test.java:

public class Test {
    public Test(){
    //somehow find the class where I came from :(
    }
}
Chris Barlow
  • 3,274
  • 4
  • 31
  • 52
cyentw
  • 41
  • 3
  • 5
    No, there's no way of doing that without either explicitly passing a value to the constructor or constructing a stack trace - which may be inaccurate due to JIT inlining. – Jon Skeet Jun 13 '14 at 17:00
  • 3
    Noo! This design sounds like a nightmare :| – user2864740 Jun 13 '14 at 17:02
  • Please be aware that doing this is a huge violation of object oriented principles. If nothing else it will force you to rewrite the class' constructor if you ever want to construct the class from a different location. Get the calling class to pass in an argument to say what it wants the constructor to do. – DJClayworth Jun 13 '14 at 18:06

2 Answers2

3

Assuming that you can't pass in the Class object of your caller: Other than the fact that the very need for this implies a rather obnoxious design, then, the only way you can do this is to construct an exception and inspecting the last stack trace element in it:

Exception e = new Exception();
StackTraceElement[] elements = e.getStackTrace();

The first element in the array is what you're looking for.

(Updated following a comment) This won't work consistently in JITted environments.

Isaac
  • 16,458
  • 5
  • 57
  • 81
  • Works great, thanks! And yes, I know my design is flawed :) – cyentw Jun 13 '14 at 17:13
  • @cyentw I know it works. But please, don't do this. – Isaac Jun 13 '14 at 17:26
  • I actually use this when initialising loggers - I think this is actually a good use case for it (calling `Logger.getLogger()` from each class is much less prone to error than manually specifying class names in each one, since inevitably one will get copied over with the wrong name!) – Michael Berry Jun 13 '14 at 21:37
  • @berry120 so perhaps people should be educated to not copy & paste code. – Isaac Jun 13 '14 at 21:42
  • @berry120 plus, you're setting yourself up for a failure due to JIT. Unlikely, but still theoretically possible. – Isaac Jun 13 '14 at 21:44
  • @Isaac Sure, people *shouldn't* copy and paste code, but theory and practice differ on that front! I'm pretty sure the JIT likelihood is much less than the likelihood of someone leaving a bogus class name somewhere. Accept it's not perfect though, should probably look at changing it at some point... – Michael Berry Jun 13 '14 at 21:48
  • @berry120 how about suggesting that class-level logging becomes a standard, built-in operation of `java.lang.Object`? :-) – Isaac Jun 13 '14 at 21:52
  • @Isaac Yes, that would be very nice. Again though, in practice I very much doubt that will happen! – Michael Berry Jun 13 '14 at 21:56
2

You can use something like this:

public class Main {
    public static void main(String[] args) {
        new Main().runMe();
    }

    public void runMe() {
        new Test(this.getClass());
    }
}

class Test {
    public Test(Class clazz) {
        System.out.println("I was invoked from '" + clazz.getCanonicalName() + "' class.");
    }
}

Prints:

I was invoked from 'Main' class.

DmitryKanunnikoff
  • 2,226
  • 2
  • 22
  • 35