4

我有一个超类,然后是几个子类,如下所示:

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>();
}

如何在不手动创建每个子类的实例并将其添加到列表的情况下“神奇地”添加Ato的所有子类的实例?list也许使用初始化块?

编辑

我如何访问并不重要listManager我将其编辑为静态的。

4

7 回答 7

2

(第二次尝试 - 我的第一次尝试是基于对问题的误解。)

我假设您想要做的是建立一个(静态)列表:

  • 恰好包含每个子类的一个实例,
  • 提前创建和填充,并且
  • 不涉及在每个子类中创建/将自身实例添加到列表中的代码。

首先,实例初始化程序块不会这样做。当您创建一个实例时会运行一个实例初始化程序......并且new类(即每个子类)必须有一些东西才能发生这种情况。

我认为唯一可行的方法是编写一些毛茸茸的反射代码:

  • 遍历类路径上的所有类,
  • 加载每个使用Class.forName(),
  • 反射性地测试该类是否是A,
  • 如果是,则反射性地调用类无参数构造函数并将结果实例添加到“列表”中。

这是(IMO)非常hacky!而且它会很昂贵......除非您可以限制需要搜索这些子类的“包空间”。


实际上,这可能是一个可以使用enum... 更好地解决的问题,特别是如果子类没有需要不同方法实现的行为差异。(例如,您的getValue()方法可能只返回一个私有变量......您使用构造函数初始化。)请参阅@Paul Bellora 的答案。

(如果需要某些子类的多个实例,则阻止它适用的事情是。这是不可能的enums。)

于 2013-06-21T02:50:05.743 回答
1

每个类将代表一个命令。

根据您对问题的描述,听起来A可能是一个enum

public enum A {

    B(1) {
        @Override
        public void execute() {
            //code and stuff
        }
    },
    C(123) {
        @Override
        public void execute() {
            //code and stuff
        }
    },
    D(15234) {
        @Override
        public void execute() {
            //code and stuff
        }
    };

    private final int value;

    private A(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public abstract void execute();
}

现在,每个命令只有一个实例,您可以轻松地使用A.values().

于 2013-06-21T03:04:02.037 回答
0

如何使用类路径扫描器自动检测您的目标类:

   List<Class<?>> classes = CPScanner.scanClasses(new ClassFilter().packageName("com.foo.*").superClass(A.class));

由于您已经获得了目标类,因此您可以使用 newInstance 方法轻松地初始化它们。

顺便说一下,使用下面的 Maven 依赖项来使用给定的代码片段:

<dependency>
    <groupId>net.sf.corn</groupId>
    <artifactId>corn-cps</artifactId>
    <version>1.1.1</version>
</dependency>

干杯。

于 2013-06-24T15:43:21.130 回答
0

虽然它不太有意义......您可以做的一种方法是,做类似于 Spring 的组件扫描的事情:利用类似的事情PathMatchingResourcePatternResolver并找出所有可能的类。如果那是 A 的子类,则遍历它们并添加到列表中。

于 2013-06-21T02:47:36.560 回答
0

可能是这样的:

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(new A());在子类化时处理了。但我不知道这是不是你要找的......


编辑 :

我如何访问 Manager 中的列表并不重要。我将其编辑为静态的。

如果您不关心使用单例,这里有一个非常基本的实现:

但是请阅读单例有什么不好

public class Manager {
   private static Manager instance = null;
   protected Manager() {
      // Exists only to defeat instantiation.
   }
   public static Manager getInstance() {
      if(instance == null) {
         instance = new Manager();
      }
      return instance;
   }
}

然后:

public abstract class A {
    public A() {
        Manager.getInstance().list.add(this);
    }
    public abstract int getValue();
}

public class B extends A {
}

但是,这又是一个非常不令人满意的设计......

于 2013-06-21T02:52:02.153 回答
0

1)您需要找到类 A 的所有可用子类。为此,您需要扫描 Java 类路径上的所有类。为了使事情更容易,我们可以假设所有子类都与 A.class 位于同一位置。A 应该在 jar 或文件夹中。我们可以找出它的实际位置为

URL url = A.class.getProtectionDomain().getCodeSource().getLocation();

2) 假设它是一个文件夹,例如file:/D:/workspace1/x/target/classes/。现在我们应该遍历这个文件夹和子文件夹中的所有 .class 文件。为此,我们可以使用 File.listFiles 或 Java 7 NIO2。我们有 2 个选择

a) 加载每个类并检查其超类

   Class cls = Class.forName();
   if (cls.getSuperClass() == A.class) {
     ...

b) 使用 javaassist 框架http://www.javassist.org或类似的直接处理类文件

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();

假设所有子类都有无参数构造函数

于 2013-06-21T03:31:11.350 回答
0

这是一种有点骇人听闻的方法,但是如果您的所有子类都在一个文件夹中(实际的类文件),您可以遍历文件夹中的文件并使用ClassLoader. 你的代码看起来像 -

for(String className : classNames){
    Class clazz = classLoader.loadClass(className);
    list.add(clazz.newInstance());
}

查看ClassLoader API了解更多信息。还要记住,这不是很有效,但如果你只是这样做一次,你应该没问题。

于 2013-06-21T02:45:10.627 回答