When a subclass inherits main() from a superclass, is it possible to determine the actual class invoked on the command-line? For example, consider the following two classes, in which main is implemented by A and inherited by B:
public class A {
public static void main(String[] args) throws Exception {
// Replace with <some magic here> to determine the class
// invoked on the command-line
final Class<? extends A> c = A.class;
System.out.println("Invoked class: " + c.getName());
final A instance = c.newInstance();
// Do something with instance here...
}
}
public class B extends A {
}
We can invoke B successfully (i.e., B does ‘inherit’ main – at least in whatever sense static methods can be inherited), but I have not found a method to determine the actual class invoked by the user:
$ java -cp . A
Invoked class: A
$ java -cp . B
Invoked class: A
The closest I’ve come is to require that the subclass implement main() and call a helper method in the superclass, which then reads the thread stack to determine the calling class:
public class AByStack {
public static void run(String[] args) throws Exception {
// Read the thread stack to find the calling class
final Class<? extends AByStack> c = (Class<? extends AByStack>)
Class.forName(Thread.currentThread().getStackTrace()[2].getClassName());
System.out.println("Invoked class: " + c.getName());
final AByStack instance = c.newInstance();
// Do something with instance here...
}
public static void main(String[] args) throws Exception {
run(args);
}
}
public class BByStack extends AByStack {
public static void main(String[] args) throws Exception {
// Call the master 'run' method
run(args);
}
}
This method works:
$ java -cp . AByStack
Invoked class: AByStack
$ java -cp . BByStack
Invoked class: BByStack
But I’d really like to eliminate the requirement that subclasses implement main() (yes, call me picky…). I don’t mind if it requires some ugly code, since it will be implemented once and buried in the base class, and I’m mostly interested in Sun/Oracle VMs, so I’d be willing to consider using a private sun.misc class or something similar.
But I do want to avoid platform-dependencies. For example, on Linux, we can look at /proc/self/cmdline, but that’s of course not portable to Windows (I’m not sure about Mac OS – I don’t have my Mac with me at the moment to test this trick). And I think JNI and JVMTI are out for the same reason. I might be wrong about JVMTI, but it looks to me like it would require a C wrapper. If not, perhaps we could use that interface somehow.
This question was asked years ago at http://www.coderanch.com/t/375326/java/java/Getting-command-line-class. The best answer there required a static initializer block in each subclass – a different, but similar requirement on the subclass author to the main calling run() solution I demonstrated. But I haven’t seen more recent discussions; I’m hopeful that current VMs might allow access to information that wasn’t available at the time of that discussion.
Since I’ve wasted considerably more time investigating than the problem warranted, I’ll post my conclusions here.
First, to try to reiterate the rationale for the question:
I have abstracted argument-handling, I/O, and other shared tasks into an abstract superclass, which I expect others to extend. After performing argument parsing and shared setup, a static method in the superclass instantiates an instance of the subclass and calls its run() method.
Authors of subclasses are encouraged to implement public static void main(String[]) and call the superclass’s primary entry point. But, unlike the requirement that all subclasses implement run(), we cannot enforce that requirement statically at compile time (since Java has no concept of an abstract static method).
So I’m trying to implement a main(String[]) method in the superclass which can determine the name of the subclass which was requested on the command-line and instantiate the appropriate class.
I’ve found two methods, both specific to the Sun / Oracle JVM.
The first uses internal sun.jvmstat classes:
The second uses Sun’s jps utility:
Hopefully my wasted time will prove helpful to someone else.