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 #.
fonte
Respostas:
Eu uso org.reflections :
Outro exemplo:
fonte
animals.add( new c() );
do seu código), porque enquantoClass.newInstance()
existe, você não tem garantia de que a classe específica tenha um construtor sem parâmetros (o C # permite exigir isso em um genérico)A maneira Java de fazer o que você deseja é usar o mecanismo ServiceLoader .
Além disso, muitas pessoas criam seus próprios arquivos tendo um local de caminho de classe conhecido (por exemplo, /META-INF/services/myplugin.properties) e, em seguida, usando ClassLoader.getResources () para enumerar todos os arquivos com esse nome em todos os jars. Isso permite que cada jar exporte seus próprios provedores e você pode instancia-los refletindo usando Class.forName ()
fonte
Pense nisso de um ponto de vista orientado a aspectos; Na verdade, o que você quer fazer é conhecer todas as classes em tempo de execução que estenderam a classe Animal. (Acho que é uma descrição um pouco mais precisa do seu problema do que o seu título; caso contrário, não acho que você tenha uma pergunta em tempo de execução.)
Então, o que eu acho que você deseja é criar um construtor de sua classe base (Animal) que adicione à sua matriz estática (eu prefiro ArrayLists, eu mesmo, mas cada uma na sua) o tipo da classe atual que está sendo instanciada.
Então, aproximadamente;
Obviamente, você precisará de um construtor estático no Animal para inicializar o InstantiatedDerivedClass ... Acho que isso fará o que você provavelmente deseja. Observe que isso depende do caminho da execução; se você tiver uma classe Dog derivada de Animal que nunca é invocada, não a terá em sua lista de Classes de Animais.
fonte
Infelizmente, isso não é totalmente possível, pois o ClassLoader não informa quais classes estão disponíveis. Você pode, no entanto, chegar bem perto fazendo algo assim:
Edit: johnstok está correto (nos comentários) que isso funciona apenas para aplicativos Java independentes e não funciona em um servidor de aplicativos.
fonte
URL[]
mas nem sempre, para que não seja possível) da hierarquia do ClassLoader. Frequentemente, você deve ter permissão, pois normalmente vocêSecurityManager
carregaria dentro da JVM.Você pode usar o ResolverUtil ( fonte bruta ) do Stripes Framework
se precisar de algo simples e rápido sem refatorar nenhum código existente.
Aqui está um exemplo simples que não carregou nenhuma das classes:
Isso também funciona em um servidor de aplicativos, já que foi onde ele foi projetado para funcionar;)
O código basicamente faz o seguinte:
ClassLoader#loadClass(String fullyQualifiedName)
Animal.class.isAssignableFrom(loadedClass);
fonte
O mecanismo mais robusto para listar todas as subclasses de uma determinada classe é atualmente o ClassGraph , porque ele lida com a maior variedade possível de mecanismos de especificação de caminho de classe , incluindo o novo sistema de módulo JPMS. (Eu sou o autor.)
fonte
O Java carrega dinamicamente as classes, portanto seu universo de classes seria apenas aquelas que já foram carregadas (e ainda não foram descarregadas). Talvez você possa fazer algo com um carregador de classes personalizado que possa verificar os supertipos de cada classe carregada. Eu não acho que exista uma API para consultar o conjunto de classes carregadas.
fonte
usa isto
Editar:
fonte
Obrigado a todos que responderam a esta pergunta.
Parece que este é realmente um osso duro de roer. Acabei desistindo e criando uma matriz estática e getter na minha classe base.
Parece que o Java simplesmente não está configurado para auto-descoberta do jeito que o C # é. Suponho que o problema é que, como um aplicativo Java é apenas uma coleção de arquivos .class em um diretório / arquivo jar em algum lugar, o tempo de execução não conhece uma classe até que seja referenciada. Naquele momento, o carregador o carrega - o que estou tentando fazer é descobri-lo antes de fazer referência a ele, o que não é possível sem sair do sistema de arquivos e procurar.
Eu sempre gosto de código que pode se descobrir, em vez de eu ter que contar sobre si mesmo, mas infelizmente isso também funciona.
Obrigado novamente!
fonte
Usando o OpenPojo, você pode fazer o seguinte:
fonte
Esse é um problema difícil e você precisará descobrir essas informações usando a análise estática, que não está disponível facilmente no tempo de execução. Basicamente, obtenha o caminho de classe do seu aplicativo, verifique as classes disponíveis e leia as informações de bytecode de uma classe da qual a classe é herdada. Observe que uma classe Dog não pode herdar diretamente de Animal, mas pode herdar de Pet, que por sua vez é herdada de Animal, portanto, você precisará acompanhar essa hierarquia.
fonte
Uma maneira é fazer com que as classes usem inicializadores estáticos ... Não acho que sejam herdados (não funcionará se forem):
Requer que você adicione esse código a todas as classes envolvidas. Mas evita ter um grande laço feio em algum lugar, testando todas as classes em busca de filhos de Animal.
fonte
Resolvi esse problema de maneira elegante usando as Anotações no nível do pacote e, em seguida, fazendo com que a anotação tivesse como argumento uma lista de classes.
Encontre classes Java implementando uma interface
As implementações apenas precisam criar um package-info.java e inserir a anotação mágica na lista de classes que eles desejam suportar.
fonte