4

I have a superclass, and then several subclasses, like this:

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

There are about 100 or so subclasses. I also have a manager:

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

How can I "magically" add an instance of all subclasses of A to list without manually creating an instance of every single subclass and adding it to the list? Perhaps with using an Initialization Block?

EDIT

It's not important how I access list in Manager. I edited it to be static.

blake305
  • 2,196
  • 3
  • 23
  • 52
  • 4
    That sounds like an unusual thing to do. What are you actually trying to achieve? Could you explain the problem you're trying to solve? – SimonC Jun 21 '13 at 02:34
  • First question is of course: why? What are you doing that requires you do this. Second question would be: why? The list manager only cares about A, and will return any item pulled from it as cast to type A. – Mike 'Pomax' Kamermans Jun 21 '13 at 02:35
  • Each class has an implementation... let me edit – blake305 Jun 21 '13 at 02:36
  • Each class is going to represent a command. There will be a method for getting the name of the command, a method that gets called whenever the command is executed, and a few other things. I don't want to have to do `list.add(new D());` with the name of each command every time I create a new command or delete a command; – blake305 Jun 21 '13 at 02:38
  • @RyanThames Please explain a little better. I want to be subclassed because each command can be executed when typecasted to the superclass – blake305 Jun 21 '13 at 02:41
  • @blake305 - The array seems to be a holder of all objects of type A. I would pass the list to constructor of A and add itself to the list. For really find implementations of interface, see some ideas in http://stackoverflow.com/questions/435890/find-java-classes-implementing-an-interface – Jayan Jun 21 '13 at 02:45

7 Answers7

2

(2nd attempt - my first attempt was based on a misunderstanding of the Question.)

I'm assuming that what you want to do is build a (static) list that:

  • contains exactly one instance of each of the subclasses,
  • is created and populated ahead of time, and
  • doesn't involve code in each subclass creating / adding an instance of itself to the list.

Firstly, an instance initializer block won't do this. An instance initializer is run when you create an instance ... and something has to new the class (i.e. each of the subclasses) for this to happen.

I think the only viable approach is to write some hairy reflective code that:

  • iterates over all classes on the classpath,
  • loads each one using Class.forName(),
  • reflectively tests to see if the class is a subclass of A,
  • if it is, reflectively invokes the classes no-args constructor and adds the resulting instance to "the list".

This is (IMO) pretty hacky!! And it is going to be expensive ... unless you can limit the "package space" that needs to be searched for these subclasses.


Actually, this could be a problem that would be better solved using an enum ... especially if the subclasses don't have behavioural differences that require different method implementations. (For instance your getValue() method could just return a private variable ... that you initialize using a constructor.) See @Paul Bellora's answer.

(The thing that would prevent this from being applicable would be if there needed to be multiple instances of some of the subclasses. That's not possible with enums.)

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • The initializer will never get called though. I guess I need a way to automatically call the initializer of all subclasses of `A` – blake305 Jun 21 '13 at 02:51
1

Each class is going to represent a command.

Based on the description of your problem, it sounds like A could be an 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();
}

Now, there is exactly one instance of each command, and you can easily iterate commands with A.values().

Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
0

This is a bit of a hackish way to do it, but if all your subclasses are in one folder (the actual class files) you could iterate over the files in the folder and use the ClassLoader. You code would look something along the lines of -

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

Look at the ClassLoader API for more info. Also keep in mind that this is not very efficient, but if you are just doing this once you should be fine.

David says Reinstate Monica
  • 19,209
  • 22
  • 79
  • 122
  • This would work, if I had a way to get a list of the classnames. I can't get the name of the class without initializing it manually for some reason. – blake305 Jun 21 '13 at 02:49
  • @blake305 Like I said before, if you know where all the actual class files are you can just make a file object to read them all to create your list of class names. – David says Reinstate Monica Jun 21 '13 at 02:53
  • @blake305 you also might want to look at [this](http://stackoverflow.com/questions/492184/how-do-you-find-all-subclasses-of-a-given-class-in-java) – David says Reinstate Monica Jun 21 '13 at 03:01
0

Although it doesn't quite make sense... one way you can do is, do things similar to Spring's component scanning: make use of things like PathMatchingResourcePatternResolver and find out all possible classes. Iterate through them and add to list if that is a subclass of A.

Adrian Shum
  • 38,812
  • 10
  • 83
  • 131
0

Could be like this :

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

This way you never again have to deal with m.list.add(new A()); while subclassing. But I don't know if this is what you are looking for...


EDIT :

It's not important how I access list in Manager. I edited it to be static.

If you don't care about using singletons, here is a very basic implementation:

But read 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;
   }
}

Then:

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

public class B extends A {
}

But, again this is very not satisfying as a design...

Community
  • 1
  • 1
Gauthier Boaglio
  • 10,054
  • 5
  • 48
  • 85
  • The initializer will never get called though. I guess I need a way to automatically call the initializer of all subclasses of `A` – blake305 Jun 21 '13 at 02:52
  • It seems to me that what you trying to achieve could be satisfied by the singleton design pattern. And for some ~good reasons, this design is not recommended. That is why the manager should be passed to any of your instances. The `magic` could come from singleton way of thinking but I suggest to read pro/against treads before. http://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons. – Gauthier Boaglio Jun 21 '13 at 02:58
  • With Singleton design you would never have to pass the manager and thus never have to redefine the subclasses constructors. `Would you like an example ?` (or may be you are already familiar with it). Singletons are ~bad, but I personally still use them in some rare circumstances... – Gauthier Boaglio Jun 21 '13 at 03:07
0

1) You need to find all available subclasses of class A. For that you need to scan all classes on the Java classpath. To make things easier we can assume that all subclasses are in the same location as A.class. A is supposed to be in a jar or in a folder. We can find out its actual location as

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

2) Lets assume that it is a folder, eg file:/D:/workspace1/x/target/classes/. Now we should walk thru all .class files in this folder and subfolders. We can use File.listFiles or Java 7 NIO2 for that. We have 2 options

a) load each class and check its superclass

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

b) use javaassist framework http://www.javassist.org or similar to work with class file directly

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

option b is loads only the classes you actually need, option a is simpler but it loads all classes in the folder

In both cases you create of an instance as

A a = (A) cls.newInstance();

assuming that all subclasses have no-arg constructor

Evgeniy Dorofeev
  • 133,369
  • 30
  • 199
  • 275
0

How about using a class path scanner to automatically detect your target classes :

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

Since you've got the target classes, you can easily initialize them by using newInstance method.

By the way use the maven dependency below to use the given snippet:

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

Cheers.

Serhat
  • 222
  • 4
  • 2