Problema dificil com tipologias (o coisa chata esses <T> do java)

8 respostas
Lavieri

Bom pessoal, estou tendo um problema com tipologia ... e esta dificil de resolver, se alguem puder por favor me ajuda...

eu tenho um método, que tem 2 parametros.... o 1° tem que ser uma classe do tipo T onde T assina IEntitySignature, e o segundo parametro é uma coleção de tipo também T .... é imperativo, que tanto a classe como a coleção sejam da mesma entidade. Este método é parte dum repositorio de entidades, que persiste o banco de dados.
public &lt;E extends IEntitySignature&gt; boolean contaisAll(Class&lt;E&gt; entityClass,Collection&lt;E&gt; entitys) {
		return DataBase.getCurrentEntityRepository(entityClass).contaisAll(entitys);
	}
//...

//Exemplo de uso:
Collection&lt;Aluno&gt; alunos = getListaParticularDeAlunosX();
boolean thisListIsPersistent =  contaisAll(Aluno.class,alunos);

eu tenho 1 objeto, que guarda coleções de entidades... esse objeto é um manager, que organiza bem direitinho as coleções.... ele separa as entidades por suas classes, em coleções internas...

Neste objeto os métodos relevantes ao meu problema são 1°) método que retorna um Iterator de classes, que são as classes das entidades.... todas as classes deste iterator assinam a interface IEntitySignature 2°) método que retorna uma coleção de entidades de uma classe especifica... todas as entidades da coleção são do mesmo tipo da classe enviada 3°) método que adciona uma entidade ao manage... ele internamente busca a coleção da classe q a entidade pertence, e a coloca nesta coleção.
public Iterator&lt;Class&lt;? extends IEntitySignature&gt;&gt; getEntityClasses() {
		return collectionManager.keySet().iterator();
	}

	public &lt;T extends IEntitySignature&gt; Collection&lt;T&gt; getEntityCollection(Class&lt;T&gt; forThisClass) {
		return (Collection&lt;T&gt;)collectionManager.get(forThisClass);
	}

	public void addEntity(IEntitySignature entity) {
		if (!contaisCollectionFor(entity)) createCollectionFor(entity);
		getCollection(entity).add(entity);
	}

//exemplo de uso (dentro do objeto, só existe 1 coleção para cada tipo de classe)
//...
objManager.addEntity(aluno1); //adciona na coleção de alunos.
objManager.addEntity(curso1); //adciona na coleção de cursos.
objManager.addEntity(aluno2); //adciona na coleção de alunos.
objManager.addEntity(aluno4); //adciona na coleção de alunos.

//Percorre a lista de coleções
for (Class classe: objManager.getEntityClasses() {
	System.out.println("Os "+ classe.getName() +" são:");
	//Percorre cada entidade dentro da coleção
	for (Object entity : getEntityCollection(classe) ) {
		System.out.println(entity.toString());
	}
}
//o resultado do console será:

/**
Os model.entity.Aluno são:
João da silva
João dos santos
José da silva

Os model.entity.Curso são:
Ciências da computação
 */

Bom meu problema esta no seguinte uso, estou implementado no repositorio um método, que pega um Manage destes de coleções...
Verifica quais classes ele contem... e manda cada coleção de cada classe para seu devido método...

Ele deveria funcionar assim
1 - pegar a lista de classes contidas no manager (são Keys para buscar as coleções)
2 - Para cada uma das chaves fazer o seguinte
. 2.1 - buscar a coleção daquela calsse
. 2.2 - testar se existe cada uma das coleções usando do repositorio contaisAll(classe,collection)
. 2.3 - se qualquer coleção retornar false através do contaisAll(classe,collection), parar o loop, e retornar que false.
3 - retornar verdade caso todas as coleções estiverem continas no repositorio.

Meu problema é o seguinte... getEntityClasses(); retorna apenas classes que implementam IEntitySignature
se eu fizer getEntityCollection(classe) de qualquer uma das classes vou objetar a sua coleção... porem! não consigo pegar a lista, de classes, percorrer a lista, e mandar a coleção para cada containsAll() .... ele fala que os tipos não são compativeis...

public boolean contaisAll(EntityCollectionManager manager) {
		boolean contais = false;
		Iterator&lt;Class&gt;&lt;? extends IEntitySignature&gt;&gt; entityClassList = manager.getEntityClasses();
		boolean moreTeste = entityClassList.hasNext();
		while(moreTeste) {
			Class&lt;? extends IEntitySignature&gt; entityClass = entityClassList.next();
			contais = contaisAll(entityClass, manager.getEntityCollection(entityClass));
			//se não contiver a coleção, ou se não houver mais listas ele sai do loop
			moreTeste = contais && entityClassList.hasNext();
		}
		return contais;
	}

O problema esta que.... contaisAll(entityClass, manager.getEntityCollection(entityClass)); não é acieto pelo compilador...
Ele normalmente deveria aceitar... visto que contaisAll(Class<T>,Collection<T> ) exige classe e coleção do mesmo tipo, o que é verdade
quando faço manager.getEntityCollection(entityClass) recebo uma coleção do mesmo tipo de entityClass...

a unica falha esta em..."entityClass" é do tipo <? extends IEntitySignature> ou seja... é um tipo generico... invalidado a operação...
Eu não tenho como saber qual classe ta vindo, a não ser em tempo de execução... não sei como fazer um caster... sem saber exatamente qual classe vai vim...

Alguem me ajuda plix!!!!

8 Respostas

J

Lavieri:

Tudo isso está na classe *Manager, certo ? Defina a classe como EntityCollectionManager e
defina os métodos em função do parametro da classe,
e não em cada um dos métodos.

+1 coisa, no método containsAll(EntityCollectionManager), por que não usa o “enhanced for” ? O código fica bem mais limpo

Jorge

Lavieri

Jorge Diz:
Lavieri:

Tudo isso está na classe *Manager, certo ? Defina a classe como EntityCollectionManager e
defina os métodos em função do parametro da classe,
e não em cada um dos métodos.

+1 coisa, no método containsAll(EntityCollectionManager), por que não usa o “enhanced for” ? O código fica bem mais limpo

Jorge

Noop… são 3 classes ai em questão…

1° se chama DataBase… ela tem funções estaticas, é uma classe de configuração, nela esta contida todos os reposiotorios do meu sistema… Por exemplo, se eu quiser resgatar o repositorio de alunos eu faço DataBase.getCurrentEntityRepository(Aluno.class) … e o repositorio de alunos sabe como tratar as entidades alunos… com esse repositorio eu tenho acesso a todas as funções como add, remove, update, etc etc etc …

2° se chama Repositories… ela é um atalho da 1° classe… através dela, eu consigo invocar os métodos dos repositorios sem ter q ficar passando a classe… para adcionar um aluno, por exemplo, basta fazer… repositories.add(aluno1) … o que esta classe faz é simples… ela busca o repositorio correto fazendo DataBase.getCurrentEntityRepository(aluno1.getClass).add(aluno1) … é basicamento isso que ela faz, ala me poupa de chamar o repositorio separado, e funciona como um repositorio generico…

3° se chama EntityCollectionManager … esta classe organiza coleções… c eu der um manager.add(aluno1) e manager.add(curso3) e manager.add(aluno2) e manager.add(matricula2) … ele vai guardar cada qual em sua coleção correspondente… c eu fizer … manager.getEntityCollection(Aluno.class) ele vai me voltar uma Collection só com o aluno1 e aluno2 … posso fazer o mesmo pra curso e matriculas… posso tb pedir a lista de entidades contidas no manager, esta lista são todas de Classes que extende a assinatura de entidade…

Enfim … desta forma eu estou querendo implementar, na segunda classe (a de atalhos de repositorios) … métodos como… addAll(EntityCollectionManager manager) … containsAll(EntityCollectionManager manager) … removeAll(EntityCollectionManager manager) …

Esta classe repositorios teria que ver a lista de de coleções do manager… para cada coleção, invocar o método correspondente, e passar a coleção como parametro…

o problema todo está nas tipologias…

se eu tiver

repAlunos = DataBase.getCurrentEntityRepository(Aluno.class); //o comando add do repositorio só irá aceitar alunos repAlunos.addAll(Collection<Aluno> entitys);

o problema está aqui… quando eu faço…

listaDeClasses = manager.getEntityClasses(); //eu recebo 1 Iterator com a lista de classes que tem dentro do manager... while(listaDeClasses.hasnext()) { entityClass = listaDeClasses.next(); manager.getEntityCollection(entityClass); //isto vai me retornar a coleção para primeira classe de entidades //eu sei que tanto "entityClass" como "manager.getEntityCollection(entityClass)" são referentes a alunos, e são da mesma classe //porem meu compilador não aceita fazer contaisAll(entityClass, manager.getEntityCollection(entityClass)); //a tipologia dois para o compilador é <? extends IEntitySignature> e ele não entende que os 2 são da mesma classe //apesar de que "entityClass" é uma Class<Aluno> e manager.getEntityCollection(entityClass) é uma Collection<Aluno> //o fato é que como manager.getEntityClasses(); me reporta uma lista sem a tipologia definida, eu precisaria fazer um cast... //mas eu não sei fazer um cast do tipo (Class<entityClass.getClass()>)entityClass ... entendeu o problema ??

Marcelo_FS

Deixa eu ver se eu entendi… sua situação atual é a seguinte, certo?

import java.util.Collection;
import java.util.Iterator;

public class TesteTipo {

	private interface IEntidade {};
	
	private class Repositorio {
	
		public <E extends IEntidade> boolean contaisAll(Class<E> entityClass,
			 	Collection<E> entitys) {
			return false;
		}
	}
	
	private class Manager {

		 public <T extends IEntidade> Collection<T> getEntityCollection 
			(Class<T> forThisClass) {  
			 return null; 
		 }
		 
		 public Iterator<Class<? extends IEntidade>> getEntityClasses() {
			 return null;
		 }
		 
		 public void addEntity(IEntidade entity) { }
	 }
	 
	 public static void main(String[] args) {
		TesteTipo t = new TesteTipo();
		Repositorio repositorio = t.new Repositorio();
		Manager manager = t.new Manager();
		Iterator<Class<? extends IEntidade>> listaDeClasses = manager.getEntityClasses();
		while(listaDeClasses.hasNext()) {
			Class<? extends IEntidade> clazz = listaDeClasses.next();
			boolean b = repositorio.contaisAll(clazz, manager.getEntityCollection(clazz)); // essa linha dá erro
		}
	}
}

Acho que a maneira mais fácil de resolver o problema é evitar que o compilador faça a verificação de tipos…:

import java.util.Collection;
import java.util.Iterator;

public class TesteTipo {

	private interface IEntidade {};
	
	private class Repositorio {
	
		public <E extends IEntidade> boolean contaisAll(Class<E> entityClass,
			 	Collection<E> entitys) {
			return false;
		}
	}
	
	private class Manager {

		 public <T extends IEntidade> Collection<T> getEntityCollection 
			(Class<T> forThisClass) {  
			 return null; 
		 }
		 
		 public Iterator<Class> getEntityClasses() {
			 return null;
		 }
		 
		 public void addEntity(IEntidade entity) { }
	 }
	 
	 public static void main(String[] args) {
		TesteTipo t = new TesteTipo();
		Repositorio repositorio = t.new Repositorio();
		Manager manager = t.new Manager();
		Iterator<Class> listaDeClasses = manager.getEntityClasses();
		while(listaDeClasses.hasNext()) {
			Class clazz = listaDeClasses.next();
			boolean b = repositorio.contaisAll(clazz, manager.getEntityCollection(clazz));
		}
	}
}

É feio? É… mas resolve :stuck_out_tongue:

Lavieri

é MarceloS… é +ou- isso dai mesmo…

só que remover o tipo gera um outro problema…

Eu tenho 1 classe assim… Repository<E extends IEntidade> …

quando alguem faz… containsAll(Collection<Aluno> collection) por exemplo…
esse contains all… chama o repositorio Repository<Aluno> para resolver o assunto…

e o repositorio que entende de Alunos é o Repository<Aluno> … c eu mandar 1 coleção de Matriculas pra ele, vai dar merda ^^

enfim… se eu num tiver a confirmação de tipologia, não consigo invocar o repositorio correto pra tratar… lembrando que além de containsAll() tempos addAll, etc etc etc … é preciso saber o tipo, pro tratamento ser de acordo la do lado da persistencia… não sei pq não funciona

Marcelo_FS

Olha… não sei como vc implementou o método pra chamar o repositório, mas em tempo de execução, a classe que você terá chamando como “Class” é a mesma que você terá chamando como “Class<? extends AlgumaCoisa>”… tirando os <> você só evita que o compilador verifique isso pra você.

Lavieri

me tira uma dúvida.... c eu tenho um método assim....

Repositories.class
public &lt;E extends IEntitySignature&gt; void updateAll(Collection&lt;E&gt; entitys) {
       //Aqui dentro eu tenho como saber a classe E ?? o simples seria E.class ...

       //O ideal era fazer assim
       DataBase.getCurrentEntityRepository(E.class).updateAll(entitys);
}
}
preciso buscar o repositorio do tipo E ... do mesmo tipo da coleção.... só que não sei fazer isso .... fiz uma gambiarra ali... Repositories.class
@SuppressWarnings("unchecked")
public &lt;E extends IEntitySignature&gt; void updateAll(Collection&lt;E&gt; entitys){
	if (entitys.size() &gt; 0) {
		E firstElement = entitys.iterator().next();
		Class&lt;E&gt; entitysClass = (Class&lt;E&gt;)firstElement.getClass();
		DataBase.getCurrentEntityRepository(entitysClass).updateAll(entitys);
	}
}

........

E apenas a titulo de informação.... DataBase.class
public static &lt;E extends IEntitySignature&gt; Repository&lt;E&gt; getCurrentEntityRepository(Class&lt;E&gt; entityClass)
Repository<T>.class
public abstract class Repository&lt;T extends IEntitySignature&gt; {
	//...
	public final TransactionResult addOrUpdateAll(Collection&lt;T&gt; entitys)
	//...
A ressalva é... Repository<T> tem que ter tipologia... seu modo de criar é ... new Repository<T>(Class<T> classe) ... e eu preciso disso por questões de persistencia...
Marcelo_FS

Repositories.class

//public &lt;E extends IEntitySignature&gt; void updateAll(Collection&lt;E&gt; entitys) {
public void updateAll(Collection entitys) {
     Class clazz = entitys.get(0).getClass();
     DataBase.getCurrentEntityRepository(clazz).updateAll(entitys);
}
// ou ainda
public void updateAll(Collection entitys, Class clazz) {
       DataBase.getCurrentEntityRepository(clazz).updateAll(entitys);
}
public void updateAll(Collection entitys){
	if (entitys.size() &gt; 0) {
		Object firstElement = entitys.iterator().next();
		Class entitysClass = firstElement.getClass();
		DataBase.getCurrentEntityRepository(entitysClass).updateAll(entitys);
	}
}

Acredito que você não tenha maiores problemas tirando o tipo daquele método; você só vai ter um monte de "warnings" gerados pelo compilador avisando que você está fazendo operações "unchecked"…

Lavieri

O problema é que a classe Repositories é publica… é usada por outras pessoas, e algum ignorante (quando falo ignorante, falo desinformado) pode tentar enviar uma Collection de Objejetos que não assinam IEntity… isso vai gerar uma exceção em tempo de execução… ai depois o bug vai ter q ser rastreado, ate verificar que algum burrinho, fez a cagada de não checar o tipo… preferia não liberar o tipos incopativeis com o repositório… ai se alguem kizer mandar um tipo genérico mesmo assim, ai já não posso fazer nada ^^, pelomenos o aviso vai ta la no método ao olho de todos

Criado 29 de dezembro de 2008
Ultima resposta 31 de dez. de 2008
Respostas 8
Participantes 3