Java. Создание случайного объекта из списка подклассов родителя

Java 11

Можно ли как нибудь, не указывая конкретного класса, создать случайный объект из какого то, возможно, списка?

Возможно вопрос не совсем понятен, но сейчас попробую подробнее его описать.

Предположим, что у меня есть public class Animal, а также его наследники - public class Cat extends Animal, public class Dog extends Animal, public class Bird extends Animal.

В некотором методе нужно создать случайный объект класса Animal, то есть кошку, собаку или птицу. Но как это сделать? Кроме вот этого способа ничего не приходит на ум:

double rand = Math.random();
if (rand < 0.33) {
    new Dog();
} else if (rand < 0.66) {
    new Cat();
} else {
    new Bird();
}

Есть ли альтернатива этому способу? То есть, возможно ли указать список подклассов класса Animal, а потом создать случайно выбранный объект из этого списка? Если да, то как это сделать?

Пробовал сделать это через интерфейсы, сделать implements для каждого подкласса, а потом получить все подклассы этого интерфейса: UnknownInterface.class.getClasses(), но он возвращал пустой массив.


Ответы (3 шт):

Автор решения: Кирилл Малышев

Воспользуйтесь рефлексией.

class Animal {

}

class Cat extends Animal{
    public Cat(){}
} 
class Dog extends Animal{
    public Dog(){}
} 
class Bird extends Animal{
    public Bird(){}
}


class Main
{
    public static void main (String[] args) throws java.lang.Exception
    {
        Class<? extends Animal>[] classes = new Class[]{
            Cat.class,
            Dog.class,
            Bird.class
        };

        Random r = new Random();
        int ind = r.nextInt(3);
        Constructor<? extends Animal> constructor = classes[ind].getConstructor();
        Object[] arguments = new Object[]{};
        Animal animal = (Animal) constructor.newInstance(arguments);
        System.out.println(animal);

    }
}

https://ideone.com/g1XvGD

Метод getConstructor() принимает произвольное количество объектов Class<?>, которые соответствуют типам аргументов конструктора. Напрмиер:

classes[ind].getConstructor(String.class, String.class);

Сами аргементы нужно добавить в массив arguments соответственно.

→ Ссылка
Автор решения: Дмитрий Никифоров

Начиная с Java 8 можно так:

ArrayList<Supplier<? extends Animal>> knownAnimals = new ArrayList<>();
knownAnimals.add(Dog::new);
knownAnimals.add(Cat::new);

int randomIndex = ...
Animal animal = knownAnimals.get(randomIndex).get();

Рефлексия не требуется.

→ Ссылка
Автор решения: tym32167

Определим классы животных

class Animal {
}

class Cat extends Animal {
    public Cat() {
    }
}

class Dog extends Animal {
    public Dog() {
    }
}

class Bird extends Animal {
    public Bird() {
    }
}

Потом интерфейс фабрики

interface AnimalFactory {
    Animal create();
}

Накидаем фабрик

class CatFactory implements AnimalFactory {
    @Override
    public Animal create() {
        return new Cat();
    }
}

class DogFactory implements AnimalFactory {
    @Override
    public Animal create() {
        return new Dog();
    }
}

class BirdFactory implements AnimalFactory {
    @Override
    public Animal create() {
        return new Bird();
    }
}

В том числе рандомную

class RandomFactory implements AnimalFactory {
    AnimalFactory[] factories;
    Random r = new Random();

    public RandomFactory(AnimalFactory[] factories) {
        this.factories = factories;
    }

    @Override
    public Animal create() {
        int ind = r.nextInt(factories.length);
        return factories[ind].create();
    }
}

Ну и запустим это все

public static void main(String[] args) {
    RandomFactory factory = new RandomFactory(new AnimalFactory[]{
            new CatFactory(),
            new DogFactory(),
            new BirdFactory()
    });

    for(int i=0; i<10; i++) {
        Animal animal = factory.create();
        System.out.println(animal.getClass().getName());
    }
}

Вывод

com.company.Bird
com.company.Bird
com.company.Cat
com.company.Cat
com.company.Cat
com.company.Dog
com.company.Cat
com.company.Bird
com.company.Cat
com.company.Dog
→ Ссылка