如何自动将子类放入ArrayList?

时间:2013-06-21 02:33:09

标签: 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 class D extends A {
    public int getValue() {
        return 15234;
    }
}

大约有100个左右的子类。我也有一位经理:

public class Manager {
    public static ArrayList<A> list = new ArrayList<A>();
}

如何“神奇地”将A的所有子类的实例添加到list,而无需手动创建每个子类的实例并将其添加到列表中?也许使用初始化块?

修改

我在list中访问Manager的方式并不重要。我把它编辑为静态。

7 个答案:

答案 0 :(得分:2)

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

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

  • 只包含每个子类的一个实例
  • 是提前创建并填充的,
  • 不会在每个子类中创建/添加自身实例到列表中的代码。

首先,实例初始化程序块不会这样做。在创建实例时运行实例初始化程序...并且必须new类(即每个子类)才能实现此目的。

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

  • 遍历类路径上的所有类,
  • 使用Class.forName()
  • 加载每一个
  • 反射性地测试以查看该类是否是A的子类,
  • 如果是,反射性地调用类no-args构造函数并将生成的实例添加到“列表”。

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


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

(阻止这种情况适用的事情是,如果需要有一些子类的多个实例。enums无法做到这一点。)

答案 1 :(得分: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()轻松迭代命令。

答案 2 :(得分:0)

这有点像一种hackish方式,但如果所有子类都在一个文件夹(实际的类文件)中,你可以遍历文件夹中的文件并使用ClassLoader。你的代码看起来像 -

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

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

答案 3 :(得分:0)

虽然它没有多大意义......你可以做的一种方法是,做类似Spring的组件扫描:使用像PathMatchingResourcePatternResolver这样的东西,找出所有可能的类。迭代它们并添加到列表中,如果它是A的子类。

答案 4 :(得分: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中访问列表并不重要。我把它编辑为静态。

如果你不关心使用单身,这是一个非常基本的实现:

但请阅读What is bad about singletons

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 {
}

但是,作为一种设计,这又非常令人满意......

答案 5 :(得分: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();

假设所有子类都没有-arg构造函数

答案 6 :(得分: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>

干杯。