我有一堆课程,我们称他们为Foo1
,Foo2
,Foo3
。他们每个人共享一个父SuperFoo并具有类似的主要功能。每个都创建一个类的实例,然后在其上调用bar()。
基本上,我现在拥有的是:
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();
}
}
基本上,这意味着我一遍又一遍地写相同的功能。
我可以在SuperFoo中使用一个继承的main,并使用反射来创建子类的实例吗?或者是否有其他方法将这些合并为一个函数?
答案 0 :(得分:2)
除非这些main
方法实现代表了保证不同入口点的不同应用程序,否则您不需要实现多个main
方法。这应该完全解决问题。
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();
}
}
或者,您可以传递类名并使用反射:
SuperFoo superFoo = (SuperFoo) Class.forName(args[0])).newInstance();
superFoo.bar();
应用程序可以使用第一个案例的数字或第二个案例的具体类名称作为程序参数启动。
答案 1 :(得分:2)
如果您确实仍希望每个类中有main
,但让SuperFoo
创建子类实例并调用bar()
,则可以使用方法引用和{{1}来执行此操作}
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 );
}
}
方法中。例如,打开与数据库的连接。
您仍然拥有用于实例特定初始化的单个子类SuperFoo::create
方法。例如,main
,Foo1::main
和Foo2::main
可以设置(但不能打开)与不同数据库的连接。
基于欧内斯特答案中的评论,该答案提供了您的漫画家代码的链接,并仔细阅读该代码库......
看起来你想要ServiceLoader
Foo3:main
在文件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();
}
}
}
}
中,您需要以下行:
your.package.name.Foo1
your.package.name.Foo2
your.package.name.Foo3
使用您想要的Foo名称(注意:不是类名称)或名称(复数)作为参数调用META-INF/services/your.package.name.FooInterface
,或者不使用名称所有的Foo's。
请注意,Main::main
不了解class Main
或Foo1
或Foo2
。服务加载程序在类路径上搜索包含Foo3
的内容。您可以稍后使用更多META-INF/services/your.package.name.FooInterface
服务添加更多jar文件,以用于主应用程序不了解的内容(FooInterface
,Foo4
,...),这些服务将自动获得支持申请。
答案 2 :(得分:2)
孩子们,不要在家里试试。肯定不建议将此黑客用于生产代码:
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 { }
通过Foo1
等启动此功能时,它会利用启动器将加载Foo1
等事实来定位main
方法以最终调用继承的SuperFoo.main
}}。识别已加载哪个子类将使用暗反射魔法完成。
从软件设计的角度来看,一个不那么hacky但仍然有问题的解决方案是:
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; } }
为了尽量减少复制和粘贴错误(或者更准确地说是忘记调整复制的代码),您可以使用
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(); } }
但是在子类中使用main
方法肯定会更清晰:
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); }
}
现在,强制通过其中一个子类启动应用程序,即不能直接通过SuperFoo
调用它。但是这段代码需要Java 9。