2

For example.

Parent: Vehicle Children: Car, Train, Horse

I want to do the following

Vehicle randVehicle = new RandomeVehicleChildObject;

I was thinking I could do this:

Vehicle randVehicle;
Random r = new Random();
int x = r.nextInt();

if(x == someInt)
   randVehicle = new Car();
else if(x == otherNum)
   randVehicle = new Train();
else
   randVehicle = new Horse();

However, what if my class has many more children? Like 15 or 20? I feel like writing so many if-else chains or switches would be a pain. Is there a way to just do it in one line?

Joao Eanes
  • 21
  • 2
  • 5
    Why do you want an instance of a randomly chosen subclass? What are you *actually* trying to do? – Bohemian Mar 29 '21 at 03:02
  • Does this answer your question? [How do you find all subclasses of a given class in Java?](https://stackoverflow.com/questions/492184/how-do-you-find-all-subclasses-of-a-given-class-in-java) – Charlie Armstrong Mar 29 '21 at 03:09
  • 2
    @Bohemian Well, for example. When you make a new character in an RPG, you have to choose a starting class: mage, warrior, rogue, etc. But maybe you don't really care what the initial class is, so you select the "Random" button, and the program randomly assigns you a class with its respective attributes. – Joao Eanes Mar 29 '21 at 03:11
  • 1
    Also this may help: [Get all declared subclasses at runtime in Java?](https://stackoverflow.com/q/29609653/15497888) – Henry Ecker Mar 29 '21 at 03:12
  • @JoaoEanes You should post details as edits to your Question rather than as Comments. – Basil Bourque Mar 29 '21 at 03:57
  • I wonder if, in the future, the [sealed classes](https://openjdk.java.net/jeps/397) feature being previewed now in Java 16 might enable asking for a collection of the known subclasses without the complexity and risk of using reflection. – Basil Bourque Mar 29 '21 at 04:00
  • 1
    @BasilBourque [getting the known subclasses](https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/lang/Class.html#getPermittedSubclasses()) *is* a reflection operation. It doesn’t guaranty that the returned classes have a suitable constructor or are instantiable at all, i.e. they might be abstract or an enum, etc. If you want a mechanism to reliably get providers of instances for a given type, you have to use the (modular) service provider architecture. For modular code, the compiler will already check the validity of `uses` and `provides` directives. – Holger Mar 29 '21 at 14:17

1 Answers1

2

I'd recommend something like the following:

class VehicleFactory {
    private final List<Supplier<Vehicle>> constructors = new ArrayList<>();

    public void addConstructors(Supplier<Vehicle>... constructors) {
        this.constructors.addAll(Arrays.asList(constructors));
    }

    public Vehicle make() {
        return constructors.get(random.nextInt(constructors.size()).get();
    }
}

Then when you want to start making vehicles:

VehicleFactory factory = new VehicleFactor();
factory.addConstructora(Car::new, Horse::new, Bike::new);
Vehicle randomVehicle = factory.make();
sprinter
  • 27,148
  • 6
  • 47
  • 78
  • 5
    I would just initialize in-line: `... constructors = List.of(Car::new, Horse::new, Bike::new);` and scrap the add method. – Bohemian Mar 29 '21 at 04:02