2

I have several abstract classes that each have several subclasses. I want to create instances of each subclass without having to copy/paste essentially the same code for what could be literally hundreds of lines, since the number of subclasses is increasing as the program becomes more complex.

For each given abstract superclass, the subclass instances will be contained in a Map (specifically a HashMap). The classes (super and sub) all have constructors without parameters.

I saw answers to this question from a decade ago saying this is not possible (or, at the very least, not advisable). I'm hoping that has changed, but I've been unable to find more recent answers.

EDIT: Since apparently my question isn't clear enough, here's an example of code (emphasis on example; this is neither a MWE nor the code that I actually have):

public abstract class A {
    public abstract void setName(String s);
    public abstract String getName();
}

public class B extends A {
    private String name = "B";

    public String getName() {
        return name;
    }

    public void setName(String s) {
        name = s;
    }
}

public class C extends A {
    private int count = 0;
    private String name = "C";

    public void increaseCount() {
        count++;
    }

    public String getName() {
        return name;
    }

    public void setName(String s) {
        name = s;
    }
}

public class Base {
    private Map<String, A> list;

    public Base() {
        list = new HashMap<>();
        A b = new B();
        list.put(b.getName(), b);
        A c = new C();
        list.put(c.getName(), c);
    }
}

public class Phone {
    private Map<String, A> list;

    public Phone() {
        list = new HashMap<>();
        A b = new B();
        list.put(b.getName(), b);
        A c = new C();
        list.put(c.getName(), c);
    }
}

I would like to avoid what you see there in the constructors for Base and Phone. Rather than having to create a new instance every single time and then add it "by hand", which will take a ridiculous amount of time and effort, I'd prefer to use an automated way to do this, if one exists.

Orion
  • 87
  • 8
  • 1
    It's definitely not advisable. *Why do you want to do this?* – chrylis -cautiouslyoptimistic- Dec 21 '19 at 21:22
  • @chrylis-onstrike- To save myself the trouble of copy/pasting hundreds of lines of code with just slight modifications that will make my code much, much longer than it needs to be and also make debugging more difficult than it needs to be. – Orion Dec 21 '19 at 21:25
  • 1
    Which constructors would you like to use? What parameters do you want to pass while invoking those constructors? Is it possible for non-abstract subclass to also have its own subclasses (if yes do you also want to instantiate all those sublcasses)? – Pshemo Dec 21 '19 at 21:39
  • It's difficult to give advice without seeing any code, but it's also possible that you could be better off using composition instead of inheritance. Please add more concrete information to the question. – Mick Mnemonic Dec 21 '19 at 21:43
  • @Pshemo The constructors without parameters. I forgot to mention that all subclasses also have constructors without parameters. I'll edit the question. – Orion Dec 21 '19 at 21:44
  • @MickMnemonic I added some example code. I honestly didn't think it'd be necessary, since the question is pretty generic. – Orion Dec 21 '19 at 21:46
  • 1
    Sounds like a job for package scan and some reflection. – Matsemann Dec 21 '19 at 21:58
  • This is a question where it helps to not over-generify into A/B/C classes; all of these classes can be simplified into one class that holds a name, and maybe a counter which you can disable. In terms of what you're actually trying to achieve, I'd imagine it'd work better to construct a class which handles many cases, barring every individual case involving unique behavioral code – Rogue Dec 21 '19 at 21:58
  • @Matsemann What's that? Can you link me to some documentation? – Orion Dec 21 '19 at 21:59
  • @Rogue As I said, it's an example of code. I thought the question by itself was pretty straightforward (how to create instances of every subtype?) and didn't need code in the first place, but I'm 100% sure you don't need me to write down random classes with completely different code in each to get the idea. – Orion Dec 21 '19 at 22:02
  • This answer seems to have the package scanning for subclasses using reflection: https://stackoverflow.com/questions/520328/can-you-find-all-classes-in-a-package-using-reflection .. `Reflections reflections = new Reflections("my.project.prefix"); Set> allClasses = reflections.getSubTypesOf(Object.class);` – antont Dec 21 '19 at 22:08
  • But why do you *need* copies of each subclass? – chrylis -cautiouslyoptimistic- Dec 21 '19 at 23:24
  • @chrylis-onstrike- Because I have other classes that will need to have maps grouping these instances together as part of how they work. I don't get why you're so focused on "why". Does the "why" affect the answer in any way? – Orion Dec 22 '19 at 00:12
  • 1
    Absolutely, because this smells like a really big [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). – chrylis -cautiouslyoptimistic- Dec 22 '19 at 00:25
  • @chrylis-onstrike- The way the other classes work requires that they have the maps with instances from all (or nearly all) the associated subclasses in them, and it's easier to remove a few objects from a `Map` _post-hoc_ than it is to add dozens (maybe more by the time I'm done) by hand. That's as simple as I can put it without posting my entire code (which I'd rather not do, because not only is it long, it's also private). – Orion Dec 22 '19 at 00:37

3 Answers3

2

I didn't quite get your question, but as far as I understand it, you want to load all subclasses of any given parent class. To achieve that you can use reflection. However, you might want to review your design first, maybe you don't even need that.

Java Reflection allows to manipulate java classes (create instances, call methods, access fields, ...) at runtime.

Here is an example of someone trying to find how to load subclasses of a given class:

How do you find all subclasses of a given class in Java?

ACV
  • 9,964
  • 5
  • 76
  • 81
  • Yes, that's exactly it. I'm honestly surprised that the question seems so confusing to so many people. I don't know how else to explain it - I want to create instances of each subclass of any given parent class. What is reflection, by the way? Someone else mentioned it, but I don't think I've heard of this before. – Orion Dec 21 '19 at 22:04
0

For your own package, this code from an answer to scanning Java packages for classes using the reflection lib seems like it should do it. Thanks to the commenter for pointing to this.

Reflections reflections = new Reflections("my.project.prefix");

Set<Class<? extends Object>> allClasses = 
     reflections.getSubTypesOf(Object.class);

From: https://stackoverflow.com/a/520339/2755690

I suppose you have some place in your software where you do init things, or what that instantiation to happen, so maybe you can do that there.

I agree with folks in the comments and other answers that maybe you want to reconsider the design. But figure that this kind of things can be useful too. In Python I've often held references to classes to instantiate random monsters in a game or whatever, and can see how it can be useful to create one of all kind in some case.

antont
  • 2,676
  • 1
  • 19
  • 21
  • 1
    The alternative would be to store each instance as a separate field (as opposed to grouping them in a ```Map```), which has the same exact problem I'm trying to avoid in the first place. Anyway, ```Reflections``` seems to be the way to go. Now I just need to learn how to use it... – Orion Dec 21 '19 at 22:18
-1

Directly, you can NOT, but indirectly, you may.

Maybe, a shorter version, of the "several lines of code, you mention".

You already answer yourself, with the Map, you need to explicitly, store or register each nonabstract descendant.

This is usually called a "data dictionary", or "metadata dictionary", and you require a collection, to save some data to be able to do this.

There is something call "type reflection", that provides special classes and object methods, like getSimpleName( ), getClass( ) and getSuperClass( ) .

You may start to declare a nonabstract class that allocates the map variable, more like a program like class:

import java.utils.map;

public class SubclassesList
{

  public static Main (...)
  {
     Map<string, Pair<object, string>> AMap =
       new Map<string, Pair<object, string>( );

     // ...

     Map = null;
   } // main

} // public class

Later, define an nonabstract class that will be the base for all other subclasses.

public /* nonabstract */ class Monster
{
    public JustDoSomething( )
    {
      System.out.println(this.getClass(  ).getSimpleName( ));
    }
} // Monster

All classes to be listed need to be able to descend from this class.

Next, as an example, lets have nonabstract subclasses from this one Monster, that we will not add any additional code related to listing, instead code specific.

public /* nonabstract */ class DireWolf 
  inherits Monster
{

  public void EatGrannies ( ) {  }

} // class

public /* nonabstract */ class Dragon
  inherits Monster
{

  public void SpitFire ( ) {  }

} // class

public /* nonabstract */ class Kraken
  inherits Monster
{

  public void CrashSeaShips( ) {  }

} // class

public /* nonabstract */ class Kitty
  inherits Monster
{

  public void Mischief ( ) {  }

} // class

Later, we need to add these classes to the "Data Dictionary" map.

import java.utils.map;

public class SubclassesList
{

  public void RegisterMonster
     (Map AMap, Monster Item)
  {
      Map.put(Item.getClass( ).getSimpleName(),
         new Pair(Item, Item.getClass( ).getSuperClass( ).getSimpleName( )));

  } //

  public static Main (...)
  {
     Map<string, Pair<Monster, string>> AMap =
       new Map<string, Pair<Monster, string>>( );

     this.RegisterMonster(new DireWolf( ));
     this.RegisterMonster(new Dragon( ));
     this.RegisterMonster(new Kraken( ));
     this.RegisterMonster(new Kitty( ));

     AMap = null;
   } // main

} // public class

Later, split the registration part, from the list.

import java.utils.map;

public class SubclassesList
{

  public void RegisterMonster
     (Map<string, Pair<Monster, string>> AMap, Monster Item)
  {
      AMap.put(Item.getClass( ).getSimpleName( ),
         new Pair(Item, Item.getClass( ).getSuperClass( ).getSimpleName( )));

  } //

 public void Registration
   (Map<string, Pair<Monster, string>> AMap)
 {
     this.RegisterMonster(new DireWolf( ));
     this.RegisterMonster(new Dragon( ));
     this.RegisterMonster(new Kraken( ));
     this.RegisterMonster(new Kitty( ));
} //

 public void ListSubclasses
   (Map<string, Pair<Monster, string>> AMap, string AParent)
 {
    for ( Pair<string, Monster> EachMonster : AMap)
    {
       if (AParent.equals(EachMonster.getkey( )))
       {
         System.out.println(EachMonster.getClass( ).getSimpleName());
       } // if
    } // for
 } //

 public void ListClasses( )
 {
   ListSubclasses
     (Monster.getClass( ).getSimpleName());
 } //

  public static void Main (...)
  {
     Map<string, Pair<Monster string>> AMap =
       new Map<string, Pair<Monster, string>( );

     this.Registration(AMap);
     this.ListClasses(AMap);

     AMap = null;
   } // main

} // public class

Now, this can be optimized by replacing the getSimpleName stored in the map, by implementing the gethashcode method, but makes it, more complicated.

Done.

umlcat
  • 4,091
  • 3
  • 19
  • 29