Reputation: 795
I have a bunch of classes let's call them Foo1
, Foo2
, Foo3
. Each of them shares a parent SuperFoo and has a similar main function. Each creates an instance of the class and then calls bar() on it.
Essentially, what I have now is:
class Foo1 extends SuperFoo {
public static void main(String[] args) {
new Foo1().bar();
}
}
class Foo2 extends SuperFoo {
public static void main(String[] args) {
new Foo2().bar();
}
}
class Foo3 extends SuperFoo {
public static void main(String[] args) {
new Foo3().bar();
}
}
Basically, this means I'm writing the same function over and over again.
Could I just have a main in SuperFoo that is inherited and uses reflection to create an instance of the subclass? Or is there some other way to merge these into a single function?
Upvotes: 0
Views: 84
Reputation: 8695
If you actually still want main
in each class, but have SuperFoo
create the subclass instance and call bar()
, you can do this with method references, and a Supplier<SuperFoo>
class SuperFoo {
static void create(Supplier<SuperFoo> supplier) {
SuperFoo foo = supplier.get();
foo.bar();
}
void bar() { ... }
}
class Foo1 extends SuperFoo {
public static void main(String[] args) {
SuperFoo.create( Foo1::new );
}
}
class Foo2 extends SuperFoo {
public static void main(String[] args) {
SuperFoo.create( Foo2::new );
}
}
class Foo3 extends SuperFoo {
public static void main(String[] args) {
SuperFoo.create( Foo3::new );
}
}
Any common pre-initialization can be moved into the SuperFoo::create
method before the supplier is invoked to create the instance of the subclass. For example, opening a connection to a database.
And you still have the individual subclass main
methods for instance specific initialization. For example, Foo1::main
, Foo2::main
and Foo3:main
could setup (but not open) connections to different databases.
Based on a comment in Ernest's answer, which gives a link to your comicgetter code, and perusing that code base...
It looks like you want a ServiceLoader
interface FooInterface {
String getName();
void bar();
}
abstract class SuperFoo implements FooInterface {
...
}
public class Foo1 extends SuperFoo {
public String getName() { return "Name1"; }
public void bar() { ... }
}
class Main /* extends nothing, implements nothing! */ {
public static void main(String args[]) {
List<String> names = List.of(args);
boolean all = names.size() == 0;
ServiceLoader<FooInterface> loader = ServiceLoader.load(FooInterface.class);
for (FooInterface foo : loader) {
if (all || names.contains(foo.getName()) {
foo.bar();
}
}
}
}
In a file META-INF/services/your.package.name.FooInterface
, you need the lines:
your.package.name.Foo1
your.package.name.Foo2
your.package.name.Foo3
Invoke Main::main
with, as arguments, the name (note: not class-name), or names (plural), of the Foo's you want, or with no name to use all Foo's.
Note that class Main
has no knowledge of Foo1
or Foo2
or Foo3
. The service loader searches for things on the class path that contain META-INF/services/your.package.name.FooInterface
. You can add more jar-files later with more FooInterface
services for things the main application does not know about (Foo4
, Foo5
, ...), and these will automatically become supported by the application.
Upvotes: 2
Reputation: 298153
Kids, don’t try this at home. This hack surely is not recommended for production code:
class SuperFoo {
public static void main(String[] args) {
try {
ClassLoader cl = SuperFoo.class.getClassLoader();
Field f = ClassLoader.class.getDeclaredField("classes");
f.setAccessible(true);
List<Class<?>> list = (List<Class<?>>)f.get(cl);
for(Class<?> c: list) {
if(c.getSuperclass() == SuperFoo.class) {
((SuperFoo)c.getDeclaredConstructor().newInstance()).bar();
return;
}
}
} catch(ReflectiveOperationException ex) {
throw new IllegalStateException(ex);
}
System.out.println("Not launched via a subclass of SuperFoo");
}
void bar() {
System.out.println(getClass().getName()+".bar()");
}
}
class Foo1 extends SuperFoo { }
class Foo2 extends SuperFoo { }
class Foo3 extends SuperFoo { }
When launching this via Foo1
, etc., it utilizes the fact that the launcher will load Foo1
, etc. for locating the main
method to eventually invoke the inherited SuperFoo.main
. Recognizing which of the subclasses has been loaded will be done with dark reflection magic.
A less hacky, but still questionable from the software design point of view, solution would be:
class SuperFoo {
static Class<? extends SuperFoo> SUB_CLASS;
public static void main(String[] args) {
if(SUB_CLASS == null)
System.out.println("Not launched via a subclass of SuperFoo");
else try {
SUB_CLASS.getDeclaredConstructor().newInstance().bar();
} catch(ReflectiveOperationException ex) {
throw new IllegalStateException(ex);
}
}
void bar() {
System.out.println(getClass().getName()+".bar()");
}
}
class Foo1 extends SuperFoo { static { SUB_CLASS = Foo1.class; } }
class Foo2 extends SuperFoo { static { SUB_CLASS = Foo2.class; } }
class Foo3 extends SuperFoo { static { SUB_CLASS = Foo3.class; } }
To minimize copy&paste errors (or more precisely forgetting to adapt the copied code), you could use
class SuperFoo {
static MethodHandles.Lookup SUB_CLASS_CTX;
public static void main(String[] args) {
if(SUB_CLASS_CTX == null)
System.out.println("Not launched via a subclass of SuperFoo");
else try {
((SuperFoo)SUB_CLASS_CTX.findConstructor(SUB_CLASS_CTX.lookupClass(),
MethodType.methodType(void.class))
.invoke()).bar();
} catch(Throwable ex) {
throw new IllegalStateException(ex);
}
}
void bar() {
System.out.println(getClass().getName()+".bar()");
}
}
class Foo1 extends SuperFoo { static { SUB_CLASS_CTX = MethodHandles.lookup(); } }
class Foo2 extends SuperFoo { static { SUB_CLASS_CTX = MethodHandles.lookup(); } }
class Foo3 extends SuperFoo { static { SUB_CLASS_CTX = MethodHandles.lookup(); } }
But it would be surely cleaner to have a main
method in the subclasses:
class SuperFoo {
protected static void doTheWork(String[] args) {
Class<? extends SuperFoo> cl =
StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
.getCallerClass().asSubclass(SuperFoo.class);
try {
cl.getDeclaredConstructor().newInstance().bar();
} catch(ReflectiveOperationException ex) {
throw new IllegalStateException(ex);
}
}
void bar() {
System.out.println(getClass().getName()+".bar()");
}
}
class Foo1 extends SuperFoo {
public static void main(String[] args) { doTheWork(args); }
}
class Foo2 extends SuperFoo {
public static void main(String[] args) { doTheWork(args); }
}
class Foo3 extends SuperFoo {
public static void main(String[] args) { doTheWork(args); }
}
Now, it is enforced that the application is launched via one of the subclasses, i.e. it can’t get invoked via SuperFoo
directly. But this code requires Java 9.
Upvotes: 2
Reputation: 45319
Unless these main
method implementations represent different applications warranting different entry points, you don't need to implement more than one main
method. And this should solve the problem altogether.
public class Main {
public static void main(String... args) {
int fooVersion = Integer.parseInt(args[0]);
SuperFoo superFoo;
switch(int) {
case 1: {
superFoo = new Foo1();
break;
}
case 2: {
superFoo = new Foo2();
break;
}
case 3: {
superFoo = new Foo3();
break;
}
default: {
throw new IllegalArgumentException();
}
}
superFoo.bar();
}
}
Alternatively, you can pass the class name and use reflection:
SuperFoo superFoo = (SuperFoo) Class.forName(args[0])).newInstance();
superFoo.bar();
And the application can be started with either a number for the first case, or a concrete class name for the second case, as program arguments.
Upvotes: 2