2

I have an Animal class which is extended by several derived Classes such Monkey Dog Cat Fish etc

In my program, I want to randomly instantiate three derived animal classes, (it doesn't matter which derivation it is - they can even allow duplicates); then plug them to a list. How can I do this?

List<Animal> animalList = new ArrayList<>();

for (int i=0;i<3;i++) { Animal animal = new Dog() or new Cat or new Dog()....;

    animallist.add(animal);
}
Nongthonbam Tonthoi
  • 12,667
  • 7
  • 37
  • 64
user6388032
  • 125
  • 8
  • Have a look at the Factory Pattern. And then create an `AnimalFactory` that randomly selects a type of animal and constructs it for you. – Justin Niessner Sep 14 '16 at 23:23
  • You could use something like [this](/a/15313028/2487517) to get all your inheritors - especially with an Factory (as @JustinNiessner suggests) to achieve this – Tibrogargan Sep 14 '16 at 23:28

5 Answers5

0

You can use the math.random function to generate a random number between 1 and however many classes you have. Then just have each number correlate with a certain class.

0

You can do in this way, using the "Random" class :

int i = new Random().getNext(3);

switch (month) {
            case 0:  animallist.add(new Dog());
                     break;
            case 1:  animallist.add(new Cat());
                     break;
            case 2:  animallist.add(new Cow());
                     break;
}
Siloé Bezerra Bispo
  • 2,056
  • 1
  • 17
  • 31
0

Just for fun (assumes all animals have a no arg constructor, boo me)

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Random;

class Animal {
   private static HashSet house = new HashSet<Class>();
   private static Random random = new Random();

   public Animal() {
       Animal.house.add(getClass());
   }

   public static List party() throws InstantiationException, IllegalAccessException {
       if (house.isEmpty()) {
           return null;
       }

       ArrayList list = new ArrayList<Animal>();
       Class[] classes = (Class[])house.toArray(new Class[0]);
       for(int i = 0; i < 3; i++) {
           list.add(classes[random.nextInt(house.size())].newInstance());
       }
       return list;
   }
}
Tibrogargan
  • 4,508
  • 3
  • 19
  • 38
  • An even bigger issue than the no-arg constructor is that the constructors must be called before the method works at all. – shmosel Sep 15 '16 at 02:37
  • @shmosel True. This is actually quite a thorny problem. Seems trivial at first glance but really isn't. Since Animal is obviously a base class (probably abstract) it's intended to have a number of derivations - possibly some not visible to the base class. The only real way to do this completely is to use something expensive like Reflections. I settled for a compromise. – Tibrogargan Sep 15 '16 at 02:44
  • It's a thorny problem by your interpretation. If we don't assume OP needs subclasses to be detected and instantiated automatically, we can avoid both problems. – shmosel Sep 15 '16 at 02:49
  • @shmosel If it's not about detecting subclasses then it's either about how to randomly select from a list or how to instantiate a class programmatically. In both cases it's probably a duplicate – Tibrogargan Sep 15 '16 at 02:52
  • Well, there are other solutions besides using a list. But I agree that it's not a very unique question (even by your interpretation) once you break it down. – shmosel Sep 15 '16 at 02:57
0

if you need to discover dynamically subclasses, Reflections

    try {
        Reflections reflections = new Reflections(Animal.class.getName().contains(".")
                ? Animal.class.getName().substring(0, Animal.class.getName().indexOf(".")) : Animal.class.getName());
        Set<Class<? extends Animal>> subs = reflections.getSubTypesOf(Animal.class);
        @SuppressWarnings("unchecked")
        Class<? extends Animal>[] subTypes = subs.toArray(new Class[subs.size()]);
        if (subTypes != null && subTypes.length > 0) {
            List<Animal> list = new ArrayList<Animal>();
            Random random = new Random();
            for (int i = 0; i < 3; i++) {
                list.add(subTypes[random.nextInt(subTypes.length)].newInstance());
            }
        }
    } catch (Exception e) {
    }
Hassen Bennour
  • 3,885
  • 2
  • 12
  • 20
-1

If you're feeling streamy, you can try this:

List<Supplier<Animal>> constructors = Arrays.asList(Dog::new, Cat::new, Fish::new, Monkey::new);
List<Animal> animalList = new Random()
        .ints(3, 0, constructors.size())
        .mapToObj(constructors::get)
        .map(Supplier::get)
        .collect(Collectors.toList());
shmosel
  • 49,289
  • 6
  • 73
  • 138
  • Poor etc! Always unappreciated – Tibrogargan Sep 15 '16 at 00:17
  • The question says there are several derived classes and does not give a complete list. Your solution only considers a subset, further it would require modification every time a new derived class was added. (But props for the method reference capture / lambda expression usage) – Tibrogargan Sep 15 '16 at 02:06
  • @Tibrogargan Nowhere does the question state it should apply *automatically* to all subtypes. If that were indeed the requirement, it would be [nearly impossible](http://stackoverflow.com/q/492184/1553851) to accomplish. – shmosel Sep 15 '16 at 02:11
  • The very question you link gives at least two ways to do it, neither of which is "nearly impossible". Expensive perhaps, but certainly not impossible. This is if you want to provide all available derivatives, not just settle for loaded ones. – Tibrogargan Sep 15 '16 at 02:21
  • If you edit the question I'll remove the downvote. Your use of lambdas does add value, even if I think your solution as a whole falls somewhat short. I could be wrong, @user6388032 does say it doesn't matter which derivation it is – Tibrogargan Sep 15 '16 at 03:09