How can I automatically subclass in an ArrayList?

I have a superclass and then several subclasses, for example:

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 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 from A to list without manually creating an instance of each individual subclass and adding it to the list? Perhaps using an initialization block?

EDIT

It doesn't matter how I access the list in Manager . I edited it as static.

+4
source share
7 answers

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

I assume that you want to create a (static) list that:

  • contains exactly one instance of each subclass,
  • created and filled ahead of schedule, and
  • does not include code in each subclass, creating / adding to it the instance itself.

First, the instance initializer block does not. The instance initializer starts when the instance is created ... and something needs a new class (i.e., each of the subclasses) for this.

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

  • iterates over all classes in the classpath,
  • downloads each using Class.forName() ,
  • reflexively checks if a class is a subclass of A ,
  • if so, it reflexively calls the no-args class constructor and adds the resulting instance to the list.

This (IMO) is pretty hacks! And it will be expensive ... if you cannot limit the "package space" that you need to search for these subclasses.


In fact, this may be a problem that will be better solved with enum ... especially if the subclasses do not have behavioral differences that require different methods to be implemented. (For example, your getValue() method may simply return a private variable ... that you initialize with the constructor.) See @Paul Bellora's answer.

(What would prevent it from being applicable would be if several instances of certain subclasses were needed. This is not possible with enums .)

+2
source

Each class will represent a team.

Based on a description of your problem, it seems that A might be 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 for each command, and you can easily A.values() over commands using A.values() .

+1
source

This is a bit of a hacky way to do this, but if all your subclasses are in the same folder (actual class files), you can ClassLoader over the files in the folder and use ClassLoader . You code would look something like this:

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

Take a look at the ClassLoader API for more information. Also keep in mind that this is not very effective, but if you just do it as soon as you are fine.

0
source

Although this is not entirely clear ... one way you can do this is to do something similar to Spring component scanning: use things like PathMatchingResourcePatternResolver and find out all the possible classes. Swipe through them and add to the list if it is a subclass of A.

0
source

Maybe so:

 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 no longer have to deal with m.list.add(new A()); during subclass. But I do not know, this is what you are looking for ...


EDIT:

It doesn't matter how I access the list in the Manager. I edited it as static.

If you don't need singletones, here is a very simple implementation:

But read What's the Bad About Singles .

 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 unsatisfying with the design ...

0
source

1) You need to find all the available subclasses of class A. To do this, you need to scan all the classes in the Java classpath. To keep things simple, we can assume that all subclasses are in the same place as A.class. It is assumed that A is in the bank or in the folder. We can find out its actual location as

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

2) Suppose this is a folder, for example, the file: / D: / workspace1 / x / target / classes /. Now we have to go through all the .class files in this folder and subfolders. For this we can use File.listFiles or Java 7 NIO2. We have 2 options

a) load each class and check its superclass

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

b) use the javaassist framework http://www.javassist.org or similarly work with the 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 only loads the classes that you really need, option a is simpler, but it loads all the classes in a folder

In both cases, you create an instance as

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

assuming all subclasses have a no-arg constructor

0
source

How to use the class path scanner to automatically detect target classes:

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

Since you have target classes, you can easily initialize them using the newInstance method.

By the way, use the maven dependency below to use this snippet:

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

Greetings.

0
source

Source: https://habr.com/ru/post/1487401/


All Articles