Eu quero fazer algo assim:
List<Animal> animals = new ArrayList<Animal>();
for( Class c: list_of_all_classes_available_to_my_app() )
if (c is Animal)
animals.add( new c() );
Então, quero examinar todas as classes no universo do meu aplicativo e, quando encontrar uma que descende de Animal, quero criar um novo objeto desse tipo e adicioná-lo à lista. Isso me permite adicionar funcionalidades sem precisar atualizar uma lista de itens. Eu posso evitar o seguinte:
List<Animal> animals = new ArrayList<Animal>();
animals.add( new Dog() );
animals.add( new Cat() );
animals.add( new Donkey() );
...
Com a abordagem acima, posso simplesmente criar uma nova classe que estenda Animal e ela será captada automaticamente.
ATUALIZAÇÃO: 16/10/2008 09:00 Hora padrão do Pacífico:
Esta pergunta gerou muitas ótimas respostas - obrigado. A partir das respostas e da minha pesquisa, descobri que o que realmente quero fazer não é possível no Java. Existem abordagens, como o mecanismo ServiceLoader do ddimitrov, que podem funcionar - mas são muito pesadas para o que eu quero e acredito que simplesmente movo o problema do código Java para um arquivo de configuração externo. Atualização 5/10/19 (11 anos depois!) Agora, existem várias bibliotecas que podem ajudar com isso, de acordo com a resposta do @ IvanNik org.reflections parece bom. Também ClassGraph de @Luke Hutchison resposta parece interessante. Existem várias outras possibilidades nas respostas também.
Outra maneira de declarar o que eu quero: uma função estática na minha classe Animal localiza e instancia todas as classes que herdam do Animal - sem nenhuma configuração / codificação adicional. Se eu tiver que configurar, é melhor instanciá-los na classe Animal de qualquer maneira. Entendo que, porque um programa Java é apenas uma federação frouxa de arquivos .class, é assim que é.
Curiosamente, parece que isso é bastante trivial em C #.