Listar todas as Classes que implementam uma dada Interface

Eu possuo uma interface MyInterface e quero obter um List<Class<? extends MyInterface>> que contenha todas as Classes no meu Projeto que implementam MyInterface diretamente ou indiretamente (com indiretamente eu refiro-me às Classes que tem uma SuperClasse ou SuperInterface que implementa a MyInterface).

Seria algo assim:

 public static <T> List<Class<? extends T>> obterClassesQueSaoDesseTipo(Class<? extends T> tipoDaMinhaInterface) {
	//TODO o que colocar aqui?
}

E seria utilizado assim:

List<Class<? extends MyInterface>> classesObtidas = obterClassesQueSaoDesseTipo(MyInterface.class);

Nota: sugestões para mudar o nome desse método "obterClassesQueSaoDesseTipo(...)" são bem vindas! rsrs

Verificar se uma classe implementa determinada interface é tranquilo.

O problema é você listar suas classes.

O Java não tem nenhum mecanismo para listar as classes de um pacote, até porque não faz sentido, já que classes podem ser carregadas sob demanda na JVM.

O que você pode fazer, mas não é nada trivial, é criar um ClassLoader customizado, que consiga percorrer seu classpath, listar os arquivos .class e a partir deles carregar objetos Class e verificar se implementam as interfaces.

Uma pergunta, por qual razão você precisa saber em tempo de execução quais as classes que implementam determinada interface?

1 curtida

Estou implementando uma espécie de “Analisador Estático de Código” para o meu Projeto, que seria um código que é chamado logo no início da execução e que - usando Reflection - verifica se as Regras que criei para as Classes do meu Projeto estão consistentes ou não. Me pareceu bem mais fácil implementar essa verificação no meu Projeto usando Reflexão, por isso coloquei para executar o conjunto de Verificações junto com o Projeto (é basicamente a primeira coisa executada) ao invés de criar um programa separado que percorre os Arquivos do Projeto e os analisa.

Até agora eu pretendo verificar pelo menos duas coisas:

  1. Qualquer classe que implementa uma uma InterfaceX poderá ser persistida num XML de modo personalizado, onde o “ID” (um int) único dessa Classe é persistido no XML e será usado no momento de carregar esse XML (afim de saber-se qual classe usar para carregar o XML); para esse mecanismo funcionar é necessário que cada uma dessas Classes tenha uma constante “public static final int ID = ...;” e que o valor de ID de cada uma dessas Classes seja único entre todas elas. Então, eu verificaria isso: “Todas as Classes que implementam a InterfaceX devem ter a Constante “ID” e devem ter um valor único (exclusivo) para essa Constante”.

  2. As Classes que implementam essa InterfaceX poderão ser editadas após alguns XMLs delas terem sido persistidos, portanto, vou colocar em cada uma delas um “public static final int SERIAL_VERSION = ...;” que diz a “Versão Atual” da Classe para que se possa saber - em Tempo de Execução - se a Versão atual da Classe é a mesma do XML (se não for laça uma Exception dizendo qual XML está desatualizado). Preciso então, verificar através de Reflection se todas as Classes do meu Projeto que implementam a InterfaceX estão declarando a constante “SERIAL_VERSION”.

Bem, o que falei acima refere-se a um código que é chamado no inicio da execução para verificar se o código do Projeto está consistente com as Regras que defini; servindo para me proteger de Bugs e evitar que eu esqueça de fazer o que tenho que fazer; esse código de verificação poderá ser removido quando o projeto estiver concluído.

Mas, quando se trata de carregar os XMLs, é importante notar que cada XML terá o “ID” da Classe a ser usada para carregá-lo e o “SERIAL_VERSION” esperado nessa Classe (se o SERIAL_VERSION for diferente lança uma Exception). Para implementar isso, acho que provavelmente vou ter que implementar com Reflexão.

A vantagem de atribuir um “ID” para cada classe e persistir esse ID no XML ao invés de persistir no XML o nome da Classe com seu Caminho no Projeto é que, se eu vier a alterar o nome da Classe ou alterar o Package no qual ela está, os XMLs não ficarão “quebrados”, pois eles referenciam ao ID da Classe que continuará o mesmo.

O que você quer dizer com “classes podem ser carregadas sob demanda na JVM”? São aqueles casos onde uma Classe pode ser até mesmo criada durante a execução e carregada e instanciada também durante a mesma execução? Bem, se é isso, muito provavelmente não precisarei fazer isso; as únicas Classes que meu projeto trabalhará - e que implementam a Minha Interface - estarão (muito provavelmente) todas dentro da pasta src do Projeto.

Veja se essa ideia funcionaria:

Crio um código que encontra a pasta src do Projeto, percorre tudo e que estiver dentro dela (e de suas subpastas) e retorna um List<File> com todos os Arquivos .java do Projeto;
Compilo em tempo de Execução cada um desses Files e gero um Objeto Class a partir dele;
Uso Reflecion para verificar se o Objeto Class obtido implementa a InterfaceX;
Armazeno num List<Class<...>> os Objetos Class que implementarem a InterfaceX; e, no fim retorno esse List<Class<...>>.

Questões:
a) no Passo, o código criado para encontrar a pasta src do Projeto e percorrer tudo dentro dela provavelmente vai falhar quando eu exportar o Software como um Jar, não é?
b) Não sei implementar o nem o Passo.

Por que não pode chamar as validações conforme for criando as classes? Sem explicar fica super esquisito essa solução. É como se não tivesse desenvolvendo de forma incremental.

A minha ideia é implementar as regras de validação do projeto de tal modo que, as regras já implementadas rodem sobre todas as classes que serão criadas depois, validando-as, sem trabalho adicional por cada classe criada. Assim, eu não preciso criar uma nova implementação de uma regra quando criar uma nova classe que precisa ser validada, nem preciso adicionar em qualquer lugar a informação de que a RegraX deve validar também a ClasseY que acabei de criar.

Então, estou tentando implementar algo como: Crio a RegraX e esta regra irá validar automaticamente todas as Classes que implementarem a InterfaceX; assim, posso ir simplesmente criando novas Classes que implementam a InterfaceX e sei que a RegraX irá - automaticamente - validar cada uma dessas novas Classes toda vez que eu rodar o Projeto.

Como Testes Unitários para validar cada Classe? Se for isso, acho que isso daria bem mais trabalho e facilitaria bugs (se eu esquecer de alguma regra ao criar o Teste).

A ideia é que as Regras de validação não são criadas todas de uma vez, mas, vão sendo adicionadas Regras conforme o design do Projeto vai sendo definido. O ponto é este: me parece mais fácil e rápido criar uma Regra que vai buscar e validar automaticamente todas as Classes que ela deve validar, ao invés de eu ter que criar/adaptar uma Regra-de-Validação para cada nova Classe criada.

Veja que, estou falando de Regras-de-Validação que podem ser aplicadas à inúmeras Classes, e não regras específicas de Classe. Além disso, para Validar se cada Classe que implementa uma InterfaceX possui uma Constante “ID” que deve ter um valor diferente de todas as outras Classes, vou precisar de algum jeito de percorrer todas essas Classes para verificar o valor da Constante “ID” de cada uma.

A solução faz mais sentido agora ou ficou ainda mais esquisita? rsrs

De fato, isto é algo que estou experimentando, mas me parece uma boa ideia.

Nao estou me referindo a testes unitários, mas no atendimento da funcionalidade para o cliente. Se amanha ele vai pedir para implementar alguma funcionalidade nova, a parte mais trabalhosa vai ser criar a entidade e suas regras, onde n linhas serão programadas. Então, que tanto transtorno seria de criar mais 1 linha para a chamada da validação dessa entidade?

Da mesma forma, se feito de forma incremental, isto poderia estar em uma classe de serviço, que seria a parte mais trabalhosa programar as novas regras solicitadas pelo cliente, onde seriam n linhas. A chamada seria só mais 1 linha. Então não entendi qual seria o transtorno de a cada solicitação de funcionalidade que demanda n linhas, fazer 1 linha a mais para chamada.

O cliente pediu isso?

Eu vou ter várias Classes que Persistem e Carregam arquivos XML, todas implementam uma InterfaceX comum, e cada Classe é responsável por Persistir/Carregar em determinado tipo (estrutura de dados) de XML.

Por causa disso, pensei colocar um ID em cada uma dessas Classes, salvar o ID no XML, e, assim, ao ler um XML, eu saberia para qual classe (a classe de qual ID) enviar o XML para o Carregamento completo dele.

Ou seja, cada XML diz o ID da Classe que eu devo utilizar para carregá-lo, daí vem a questão de cada uma dessas Classes ter seu próprio ID.

Tem que lidar com isso da mesma forma como se fossem tabelas de um banco de dados, não precisa de tanto malabarismo. Quem define estrutura de um XML é o XSD.

Quando penso em não colocar IDs nas Classes, eu penso em resolver mais ou menos assim:

public MeuObjetoCarregado carregar(int tipoDoXML, Document xml) {
    switch(tipoDoXML) {
        case 0: return ClasseA.carregar(xml);
        case 1: return ClasseB.carregar(xml);
        case 2: return ClasseC.carregar(xml);
        default: throw new Exception("Tipo desconhecido: "+tipoDoXML);
    }
}

Veja que, toda vez que criar uma ClasseD, ClasseE, etc., vou precisar voltar nesse switch e adicionar a Classe nele. Poderia até tentar resolver com um Map e outras coisas, mas sempre seria necessário “registrar” a nova Classe em algum lugar que relacione o “tipo-do-xml” com a Classe que deve carregá-lo.

Você acha a implementação boa ou ruim? Como você faria?

Outra possibilidade, seria registrar todas essas Classes numa lista, e, percorrer a Lista procurando por uma Classe que diga ser capaz de Carregar o XML do tipo em questão:

for (CarregadorDeXML c : carregadores) {
    if (c.consegueCarregar(tipoDoXML) {
        return c.carregar(xml);
    }
}

A implementação acima exigiria uma instancia de cada Carregador para chamar um método que poderia ser estático ("carregar(...)"), então não sei se realmente é uma boa ideia.

Eu usaria sob demanda. Precisando chamar ClasseA, então chamo quando precisar de A. O mesmo vale para os outros. Classe D não existe, quando existir vai entrar onde precisar.

Só não entendi você ter várias classes diferentes que retornam o mesmo tipo de objeto. Se for assim, melhor ter uma classe única de serviço que carregue esses XMLs.

http://software.clapper.org/javautil/

Faz algumas coisas que você citou, da uma verificada;

Consegui encontrar os Types que implementam uma interface especifica usando essa classe:

http://software.clapper.org/javautil/api/org/clapper/util/classutil/ClassFinder.html

edit
Obs*: Essa lib usa reflection.

Att

1 curtida