Métodos usando generics

12 respostas
P

Eaê, galera!

Criei um método que extrai dados de uma tabela de strings e gera uma lista de objetos, usando reflexão. O problema é que, até o momento, meu método retorna uma lista de Object, queria saber se é possível que ele retorne uma lista do tipo passado como parâmetro diretamente. Segue meu código abaixo.

Obrigado pela ajuda!

protected List<Object> getObjectsFromTable(
		List<List<String>> table,
		Class<?> type,
		String[] fields_names,
		Class<?>[] fields_types)
			throws Throwable {
	/* Pega os setters do objeto. */
	Method[] methods = new Method[fields_names.length];
	for (int i = 0; i < fields_names.length; i++) {
		String field_name = fields_names[i];
		String method_name = "set" + Character.toUpperCase(field_name.charAt(0)) +
				field_name.substring(1);
		
		methods[i] = type.getMethod(method_name, new Class<?>[]{fields_types[i]});
	}
	
	List<Object> result = new ArrayList<Object>();
	
	/* Percorre as linhas da tabela. */
	ListIterator<List<String>> i = table.listIterator();
	while (i.hasNext()) {
		/* Nova instância do objeto. */
		Object aux = type.newInstance();
		
		/* Pega os campos da linha atual. */
		List<String> campos = i.next();
		ListIterator<String> j = campos.listIterator();
		for (int index = 0; index < fields_names.length; index++) {
			String campo = j.next().toUpperCase();
			Class<?> tipo = fields_types[index];
			if (tipo == String.class) {
				methods[index].invoke(aux, campo);
			} else if (tipo == long.class) {
				methods[index].invoke(aux, Long.parseLong(campo));
			} else if (tipo == int.class) {
				methods[index].invoke(aux, Integer.parseInt(campo));
			} else {
				throw new IllegalArgumentException("Invalid type!");
			}
		}
		
		result.add(aux);
	}
	
	return result;
}

12 Respostas

M

pdform:
Eaê, galera!

Criei um método que extrai dados de uma tabela de strings e gera uma lista de objetos, usando reflexão. O problema é que, até o momento, meu método retorna uma lista de Object, queria saber se é possível que ele retorne uma lista do tipo passado como parâmetro diretamente. Segue meu código abaixo.

Obrigado pela ajuda!

retorne uma lista do tipo passado como parâmetro diretamente “isso me parace meio redundante”.

:arrow: http://www.artima.com/lejava/articles/neal_gafter_closures.html , estou lendo esse artigo aqui e me pareceu algo interessante ao que vc , perguntou …

P

Exatamente… na verdade, eu formulei mal a pergunta: eu queria passar o tipo uma única vez como parâmetro generics e já retornar a lista desse tipo. O melhor que consegui até agora foi isso:

protected static <T> List<T> getObjectsFromTable(
		List<List<String>> table,
		Class<? extends T> type,
		String[] fields_names,
		Class<?>[] fields_types)
			throws Throwable {
	/* Pega os setters do objeto. */
	Method[] methods = new Method[fields_names.length];
	for (int i = 0; i < fields_names.length; i++) {
		String field_name = fields_names[i];
		String method_name = "set" + Character.toUpperCase(field_name.charAt(0)) +
				field_name.substring(1);
		
		methods[i] = type.getMethod(method_name, new Class<?>[]{fields_types[i]});
	}
	
	List<T> result = new ArrayList<T>();
	
	/* Percorre as linhas da tabela. */
	ListIterator<List<String>> i = table.listIterator();
	while (i.hasNext()) {
		/* Nova instância do objeto. */
		T aux = type.newInstance();
		
		/* Pega os campos da linha atual. */
		List<String> campos = i.next();
		ListIterator<String> j = campos.listIterator();
		for (int index = 0; index < fields_names.length; index++) {
			String campo = j.next().toUpperCase();
			Class<?> tipo = fields_types[index];
			if (tipo == String.class) {
				methods[index].invoke(aux, campo);
			} else if (tipo == long.class) {
				methods[index].invoke(aux, Long.parseLong(campo));
			} else if (tipo == int.class) {
				methods[index].invoke(aux, Integer.parseInt(campo));
			} else {
				throw new IllegalArgumentException("Invalid type!");
			}
		}
		
		result.add(aux);
	}
	
	return result;
}

O problema é que eu precisso passar e type, para poder usar reflection dentro do método, o que é redundante. Como eu poderia pegar T.class?

Obrigado pela ajuda!

P

infelizmente nao da. eu escrevi um artigo exatamente sobre isso:

http://blog.caelum.com.br/2008/04/28/nao-posso-descobrir-nem-instanciar-tipos-genericos-porque/

é uma limitacao do java.

V

Sim, tem que ser redundante. Um é para o compilador saber o tipo e o outro é para que a sua lógica saiba o tipo. O compilador não é capaz de ver a sua lógica, e o type-erasure impede que a sua lógica veja o generic do compilador. Por isso tem que ser redundante.

Quiçá o java 7 resolve esse problema com reification. ERASE THE ERASURE!

M

Sim, tem que ser redundante. Um é para o compilador saber o tipo e o outro é para que a sua lógica saiba o tipo. O compilador não é capaz de ver a sua lógica, e o type-erasure impede que a sua lógica veja o generic do compilador. Por isso tem que ser redundante.

Quiçá o java 7 resolve esse problema com reification. ERASE THE ERASURE!

:arrow: Contraditório então pela a afirmação do Paulo Silveira, citado acima ???

V

Sim, tem que ser redundante. Um é para o compilador saber o tipo e o outro é para que a sua lógica saiba o tipo. O compilador não é capaz de ver a sua lógica, e o type-erasure impede que a sua lógica veja o generic do compilador. Por isso tem que ser redundante.

Quiçá o java 7 resolve esse problema com reification. ERASE THE ERASURE!

:arrow: Contraditório então pela a afirmação do Paulo Silveira, citado acima ???

Não, não é contraditório.

Ele tem que passar e type, algo que é redundante porque o é para o compilador entender e type é para a lógica do programa entender.

Não dá, no java 6, para fazer sem ser redundante. Você não pode instanciar um generic ou descobri-los por reflection porque eles são perdidos na compilação. Não estão lá nos bytecodes. A “gambiarra” para colocá-los nos bytecodes é colocar o type, um Class explícito com o qual você pode trabalhar na lógica do seu programa. O problema é que isso duplica o tipo, uma vez que e type são na verdade a mesma coisa. E como não é possível fazer o compilador entender o type, apenas o , você é obrigado a colocar os dois.

Mas, no java 7, está planejado a reificação dos generics, que basicamente faz com que o exista nos bytecodes e que por isso possa ser visto no reflection. Isso elimina a necessidade de existir o type e elimina a necessidade de haver redundância.

M

victorwss:
…Sim…(…)…
…(…)…
…Mas, no java 7, está planejado a reificação dos generics, que basicamente faz com que o exista nos bytecodes e que por isso possa ser visto no reflection. Isso elimina a necessidade de existir o type e elimina a necessidade de haver redundância…

:thumbup: Bom, eu não sei porque uma afirmação e depois algo que vem a ser de uso favorável em sua colocação, entretando acho que o Paulo Silveira poderia melhor informar no seu blog, pois simplesmente ele direciona para uma pagina http://gafter.blogspot.com/2006/11/reified-generics-for-java.html , que depois contradiz tudo sobre o que, vem a ser uma coisa para Java 5 e outra evolução no Java 7.

P

bem Marcio, nao consegui entender direito o que voce escreveu. Acho que voce nao percebeu que eu e o Victor estamos falando a MESMA coisa: eu no blog e ele naquela frase. O movimento erase the erasure teria de criar um SEGUNDO tipo de generics, ja que o generics da maneira atual que foi implementada, atraves de erasures, NAO DA para pegar o tipo generico em tempo de execucao.

Pra eles colocarem a reificacao, tera de criar esse segundo tipo de generics (sugerido como em vez de ) . Esse sim seria atraves de reificacao, que nao existe hoje.

Repetindo a minha afirmacao mais claramente: hoje em dia, com o generics como esta, nao da para fazer isso. E nem vai dar sem criar um OUTRO generics. Eles nao vao fazer o mesmo pois seria uma quebra de compatibilidade (nao da pra por reificacao em cima de erasure sem quebra de compatibilidade, nao vao fazer isso, como ja afirmaram).

P

victorwss:

Mas, no java 7, está planejado a reificação dos generics, que basicamente faz com que o exista nos bytecodes e que por isso possa ser visto no reflection. Isso elimina a necessidade de existir o type e elimina a necessidade de haver redundância.

Isso, mas nao sera a mesma sintaxe.

E infelizmente nao esta planejado, estao pensando mas muita gente fala que nao vai ter.

M

Paulo Silveira:

bem Marcio, (…)
(…)… tipo generico em tempo de execucao.

Pra eles colocarem a reificacao, tera de criar esse segundo tipo de generics (sugerido como em vez de ) . Esse sim seria atraves de reificacao, que nao existe hoje.

Repetindo a minha afirmacao mais claramente: hoje em dia, com o generics como esta, nao da para fazer isso. E nem vai dar sem criar um OUTRO generics. Eles nao vao fazer o mesmo pois seria uma quebra de compatibilidade (nao da pra por reificacao em cima de erasure sem quebra de compatibilidade, nao vao fazer isso, como ja afirmaram).

:thumbup: Ficou mais esclarecido, porque estava acreditando que a reificação já era algo aprimorado sobre que fora colocado ao Java 7, mas já que ainda não existe,a informação do victor virou tese.

Valeu !!!

S

Não é redundante. É assim mesmo que se faz. T não é passado para lado nenhum ele é apenas um marcador (não um objecto). Vc pode deixar assim.

protected static <T> List<T> getObjectsFromTable(
		List<List<String>> table,
		Class<T> type,
		String[] fields_names,
		Class<?>[] fields_types)
	
}
M

:arrow: Informação pode ser vista melhor no Blog da Caelum.

:idea: http://blog.caelum.com.br/2008/04/28/nao-posso-des...anciar-tipos-genericos-porque/

:thumbup:

Criado 10 de julho de 2008
Ultima resposta 13 de jul. de 2008
Respostas 12
Participantes 5