Java 如何自动将子类放入ArrayList?
我有一个超类,还有几个子类,如下所示:Java 如何自动将子类放入ArrayList?,java,Java,我有一个超类,还有几个子类,如下所示: public abstract class A { public abstract int getValue(); } public class B extends A { public int getValue() { return 1; } } public class C extends A { public int getValue() { return 123; } }
public abstract class A {
public abstract int getValue();
}
public class B extends A {
public int getValue() {
return 1;
}
}
public class C extends A {
public int getValue() {
return 123;
}
}
public class D extends A {
public int getValue() {
return 15234;
}
}
大约有100个子类。我还有一位经理:
public class Manager {
public static ArrayList<A> list = new ArrayList<A>();
}
公共类管理器{
public static ArrayList list=new ArrayList();
}
如何“神奇地”将A
的所有子类的实例添加到list
中,而不手动创建每个子类的实例并将其添加到列表中?也许使用初始化块
编辑
如何访问
管理器中的列表
,并不重要。我将其编辑为静态。这是一种有点粗俗的方式,但是如果所有子类都在一个文件夹中(实际的类文件),您可以迭代文件夹中的文件并使用类加载器
。你的代码看起来像是这样的-
for(String className : classNames){
Class clazz = classLoader.loadClass(className);
list.add(clazz.newInstance());
}
有关更多信息,请参阅。还请记住,这不是很有效,但如果你只是这样做一次,你应该很好 虽然没有什么意义。。。一种方法是,执行类似于Spring组件扫描的操作:使用类似于PathMatchingResourcePatternResolver
的方法,找出所有可能的类。如果这是a.的子类,则遍历它们并添加到列表中。(第二次尝试-我的第一次尝试是基于对问题的误解。)
我假设您要做的是构建一个(静态)列表:
- 仅包含每个子类的一个实例
- 提前创建和填充,以及
- 不涉及在每个子类中创建/添加自身实例到列表的代码
首先,实例初始值设定项块不会这样做。创建实例时会运行实例初始值设定项。。。必须new
类(即每个子类)才能发生这种情况
我认为唯一可行的方法是编写一些毛茸茸的反射代码:
- 迭代类路径上的所有类
- 使用
Class.forName()
加载每个
- 反射性地测试类是否是
a
的子类
- 如果是,则反射地调用classes no args构造函数,并将结果实例添加到“列表”中
这(依我看)相当骇客!!而且会很贵。。。除非您可以限制需要搜索这些子类的“包空间”
实际上,使用enum
可以更好地解决这个问题。。。特别是如果子类没有需要不同方法实现的行为差异。(例如,您的getValue()
方法可以只返回一个私有变量……您可以使用构造函数初始化它。)请参阅@Paul Bellora的答案
(如果需要某些子类的多个实例,则会阻止此操作的应用。枚举不可能做到这一点)可能是这样的:
public abstract class A {
public A(Manager m) {
m.list.add(this);
}
public abstract int getValue();
}
public class B extends A {
public B(Manager m) {
super(m);
}
}
这样你就不再需要处理m.list.add(newa())代码>子类化时。但我不知道这是否是你想要的
编辑:
如何在Manager中访问列表并不重要。我把它编辑成静态的
如果您不关心使用单例,下面是一个非常基本的实现:
但是请阅读
然后:
但是,作为一种设计,这也是非常不令人满意的…
每个类将表示一个命令
根据对问题的描述,听起来A
可能是:
现在,每个命令只有一个实例,您可以使用A.values()
1)轻松地迭代命令。您需要找到类A的所有可用子类。为此,您需要扫描Java类路径上的所有类。为了使事情更简单,我们可以假设所有子类都与.class位于同一位置。A应该放在罐子或文件夹中。我们可以找到它的实际位置
URL url = A.class.getProtectionDomain().getCodeSource().getLocation();
2) 假设它是一个文件夹,例如文件:/D:/workspace1/x/target/classes/。现在我们应该遍历此文件夹和子文件夹中的所有.class文件。我们可以使用File.listFiles或Java7NIO2来实现这一点。我们有两个选择
a) 加载每个类并检查其超类
Class cls = Class.forName();
if (cls.getSuperClass() == A.class) {
...
b) 使用javaassist框架或类似工具直接处理类文件
DataInputStream ds = new DataInputStream(new BufferedInputStream(path));
ClassFile cf = new ClassFile(ds);
String superClass = cf.getSuperClass();
if (superClass.equals("A")) {
Class cls = Class.forName(cf.getName());
...
选项b只加载您实际需要的类,选项a更简单,但它加载文件夹中的所有类
在这两种情况下,都将实例的
A a = (A) cls.newInstance();
假设所有子类都没有arg构造函数如何使用类路径扫描程序自动检测目标类:
List<Class<?>> classes = CPScanner.scanClasses(new ClassFilter().packageName("com.foo.*").superClass(A.class));
list这听起来是一件不同寻常的事情。你到底想达到什么目的?你能解释一下你要解决的问题吗?第一个问题当然是:为什么?你在做什么需要你这么做。第二个问题是:为什么?列表管理器只关心A,并将从中提取的任何项转换为类型A。每个类都有一个实现。。。让我来解释一下,每个类将代表一个命令。将有一个获取命令名的方法,一个在执行命令时调用的方法,以及其他一些东西。我不想做list.add(newd())每次创建新命令或删除命令时,使用每个命令的名称编写代码>@兰瑟姆斯,请解释清楚一点。我希望被子类化,因为每个命令都可以在类型转换到超类时执行。如果我有办法得到一个类名列表,这就行了。由于某种原因,如果不手动初始化,我无法获取类的名称。@blake305就像我之前说过的,如果你知道所有实际的类文件在哪里,你可以创建一个文件对象来读取
DataInputStream ds = new DataInputStream(new BufferedInputStream(path));
ClassFile cf = new ClassFile(ds);
String superClass = cf.getSuperClass();
if (superClass.equals("A")) {
Class cls = Class.forName(cf.getName());
...
A a = (A) cls.newInstance();
List<Class<?>> classes = CPScanner.scanClasses(new ClassFilter().packageName("com.foo.*").superClass(A.class));
<dependency>
<groupId>net.sf.corn</groupId>
<artifactId>corn-cps</artifactId>
<version>1.1.1</version>
</dependency>