Preciso de ajuda pra inferir uma classe em Java de forma dinâmica

Como eu faço pra inferir uma classe em Java. Tenho uma função que retorna uma lista, porém a classe que a List vai receber é definida dentro do método, porque pode ser mais de uma. Minha implementação abaixo funciona como eu esperava, mas eu preciso fazer um cast do chamador da função pra funcionar, e eu acho isso muito deselegante. Outra: se amanhã eu tiver outra classe no meu sistema que precise usar esse método, vou precisar adicioná-lo no IF, fazendo com que minha função seja 0% coesa.

public static List<?> obterListaDeObjetosBaseadoNaString(Class classe,
		String... linhas) {
	List<String> nomes = new ArrayList<>();
	
	for (String linha : linhas) {
		if (linha.contains(",")) {
			String[] elementosDaLinhaSeparadaPorVirgula = linha.split(",");

			for (String nome : elementosDaLinhaSeparadaPorVirgula) {
				nomes.add(nome);
			}
		} else {
			nomes.add(linha);
		}
	}
	
	if (classe == Diretor.class) {
		return nomes.stream()
				.map(Diretor::new)
				.collect(Collectors.toList());
	} else {
		return nomes.stream()
				.map(Genero::new)
				.collect(Collectors.toList());
	}

}

Tendo em vista esse problema, alguém sabe como eu posso fazer pra deixar a inferência da classe de forma dinâmica?

Isso?

import java.util.ArrayList;
import java.util.List;

public class Main {
	
	public static void main(String[] args) throws Exception {
		List<Integer> teste1 = obterListaDeObjetosBaseadoNaString(Integer.class, "1", "2", "3");
		List<Double> teste2 = obterListaDeObjetosBaseadoNaString(Double.class, "1", "2", "3");
		List<Custom> teste3 = obterListaDeObjetosBaseadoNaString(Custom.class, "1", "2", "3");
	}
	
	public static <T> List<T> obterListaDeObjetosBaseadoNaString(Class<T> classe, String ... linhas) {
		return new ArrayList<T>();
	}
	
	static class Custom {
		
	}
}

A “mágica” acontece por conta do <T> definido no método.

Leia mais sobre aqui: https://docs.oracle.com/javase/tutorial/java/generics/methods.html

1 curtida

Cara, não peguei muito bem a ideia. Até deu certo aqui, mas ainda continuo com o mesmo código só que agora com um cast no próprio método.

public static <T> List<T> obterListaDeObjetosBaseadoNaString(Class<T> classe, String ... linhas) {
	List<String> nomes = new ArrayList<>();
	
	for (String linha : linhas) {
		if (linha.contains(",")) {
			String[] elementosDaLinhaSeparadaPorVirgula = linha.split(",");

			for (String nome : elementosDaLinhaSeparadaPorVirgula) {
				nomes.add(nome);
			}
		} else {
			nomes.add(linha);
		}
	}
	
	if (classe == Diretor.class) {
		return (List<T>) nomes.stream()
				.map(Diretor::new)
				.collect(Collectors.toList());
	} else {
		return (List<T>) nomes.stream()
				.map(Genero::new)
				.collect(Collectors.toList());
	}
	
}

Para evitar esses casts (e tb vários IF-ELSE), vc pode usar expressões lambda:

public static void main(String[] args) {
	List<Diretor> diretores = obterListaDeObjetosBaseadoNaString(Diretor::new, "1", "2", "3");
	List<Genero> generos = obterListaDeObjetosBaseadoNaString(Genero::new, "1", "2", "3");
}

public static <T> List<T> obterListaDeObjetosBaseadoNaString(Function<String, T> fn, String ... linhas) {
	List<String> nomes = new ArrayList<>();

	for (String linha : linhas) {
		if (linha.contains(",")) {
			String[] elementosDaLinhaSeparadaPorVirgula = linha.split(",");

			for (String nome : elementosDaLinhaSeparadaPorVirgula) {
				nomes.add(nome);
			}
		} else {
			nomes.add(linha);
		}
	}

	return nomes.stream().map(fn).collect(Collectors.toList());
}

Em vez de usar a expressão com ::new, poderia ser assim também:

List<Diretor> diretores = obterListaDeObjetosBaseadoNaString((v) -> new Diretor(v), "1", "2", "3");
List<Genero> generos = obterListaDeObjetosBaseadoNaString((v) -> new Genero(v), "1", "2", "3");
2 curtidas

Utilizando interface funcional ficou mais simples de entender o que está sendo feito. Obrigado pela ajuda.