[AJUDA] Método genérico que retorna uma instancia

Pessoal,

quem trabalha com PMD, encontra problemas em instanciar classes dentro de loops. Para resolver esse problema, quero criar um método genérico e estático, que retorna uma instancia de uma classe.

Tentei da seguinte forma, sem sucesso:

public class RetornaInstancia {
	
	// Método Responsável por retornar a instancia de uma Classe
	public static Object getInstance(Class<?> classe){
		return classe.getClass();		
	}
}

Não funcionou pois ele retorna um Class e não um Objeto da classe.

Alguem consegue me dar uma luz???

Obrigado.

Cara só não funcinou por que você escolheu o metodo errado.

getClass vai te retornar a classe mesmo

tenta newInstance() e vai funcionar, des de que sua classe tenha uma construtor sem parametros

Olá,

[code]
public class RetornaInstancia {

// Método Responsável por retornar a instancia de uma Classe
public static Object getInstance(Class<?> classe){
	return classe.newInstance();
}

}[/code]

a Classe dever ter um construtor default, deve haver outros meios, mas eu desconheço no momento. :smiley:

[quote=Vmaia]Olá,

[code]
public class RetornaInstancia {

// Método Responsável por retornar a instancia de uma Classe
public static Object getInstance(Class<?> classe){
	return classe.newInstance();
}

}[/code]

a Classe dever ter um construtor default, deve haver outros meios, mas eu desconheço no momento. :smiley:

[/quote]

Exemplo de construção com construtores:

Class clazz = Integer.class;
Constructor c = clazz.getConstructor(String.class);
Integer integer = (Integer)c.newInstance("2");

[]´s

Um pouco além(para consulta futura)

Exemplo de construção com uma non-static inner class

Supondo que o modelo seja.

public class A{
protected class B{
}
}

Então pra inicializar B seria.

Constructor con = B.class.getConstructor(A.class);//non-static inner classes tem esse construtor escondido para fazer um A.this funcionar.
Object obj = con.newInstance(new A());//Necessario passar a instancia de A como argumento

essas soluções nao funcionam pois lançam exceções e no meu projeto existe um tratamento de exceção genério, e eu teria que saber de qual camada estou chamando o getInstance…

=////////

Não é por nada não, mas criar um método genérico para criar uma instância de uma classe, para evitar um warning do PMD, é uma coisa podre mesmo.

O mais recomendado é, para cada classe que você precisa instanciar dentro de um loop (não estou questionando por que você tem de fazer isso; normalmente o motivo é legítimo e o warning é que é podre - já pensaram em negociar com o seu cliente desativar o tal warning? ) você criar um método estático que retorne uma instância dessa classe, e que receba os parãmetros adequados. Por exemplo:

class Aluno {
    public Aluno (final String nome, final int codigo) { .... .... }
    public static Aluno getInstance (final String nome, final int codigo) {
        return new Aluno (nome, codigo);
    }
}

Eu sei que vai dar um trabalhão do cão, e que vai ativar algum outro warning do PMD, mas você dispensa o uso de reflection (que é o que o pessoal está lhe mandando fazer) para criar uma instância, o que é uma coisa que vai deixar seu programa muito lento.

[quote=ataufo]essas soluções nao funcionam pois lançam exceções e no meu projeto existe um tratamento de exceção genério, e eu teria que saber de qual camada estou chamando o getInstance…

=////////[/quote]

Cara, meu amigo é so você tratar essas exceções no teu metodo de pegar instancia mesmo. Sei que elas deviam ser uncheck, mas o mundo java é cruel mesmo.
Caso contratrio esquece!

Galera,

primeiro, obrigado pelas opiniões…

Cara concordo que é ridiculo, mas já foi negociado com o cliente e NADA… Essa solução que vc falou, foi a solução que eu propus para o meu Líder, porém ele falou que teria que ser algo genérico para não botar um método desse em cada classe de negócio. Porém, acaba que a gente tem q criar vários métodos de criar instancia em vários lugares diferentes do projeto !!! É uma merda !!!

Tratar exceções eu sei, mas eu teria que lançar essa exceção por todas as camadas do projeto, não é viável…

Diga a ele que métodos genéricos usam reflection e são intrinsecamente lentos e inseguros, portanto é melhor pôr um método desse em cada classe de negócio sim. O problema, obviamente, é alguém sair modificando todas as classes de negócios, o que você gostaria que fosse possível ser feito com um plugin do Eclipse ou NetBeans (assim como os “setters e getters” que ambos têm.)

É mas pra eu afirmar isso eu tenho que ter algum argumento técnico, e eu não saco nada de reflection… Conhece algum lugar para eu ler sobre?

Obrigado.

Escrevi este programa.

package guj;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import net.miginfocom.swing.MigLayout;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class BenchmarkConstrutorGenerico extends JFrame {

	private static final long serialVersionUID = 1L;
	private JPanel jContentPane = null;
	private JLabel lblNumeroIteracoes = null;
	private JTextField txtNumeroDeIteracoes = null;
	private JButton btnStart = null;
	private JScrollPane scpResults = null;
	private JTextArea txtResults = null;

	/**
	 * This method initializes txtNumeroDeIteracoes	
	 * 	
	 * @return javax.swing.JTextField	
	 */
	private JTextField getTxtNumeroDeIteracoes() {
		if (txtNumeroDeIteracoes == null) {
			txtNumeroDeIteracoes = new JTextField();
		}
		return txtNumeroDeIteracoes;
	}

	/**
	 * This method initializes btnStart	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getBtnStart() {
		if (btnStart == null) {
			btnStart = new JButton();
			btnStart.setText("Iniciar");
			btnStart.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					Bench bench = new Bench (Long.parseLong (txtNumeroDeIteracoes.getText()), txtResults);
					bench.test();
				}
			});
		}
		return btnStart;
	}

	/**
	 * This method initializes scpResults	
	 * 	
	 * @return javax.swing.JScrollPane	
	 */
	private JScrollPane getScpResults() {
		if (scpResults == null) {
			scpResults = new JScrollPane();
			scpResults.setViewportView(getTxtResults());
		}
		return scpResults;
	}

	/**
	 * This method initializes txtResults	
	 * 	
	 * @return javax.swing.JTextArea	
	 */
	private JTextArea getTxtResults() {
		if (txtResults == null) {
			txtResults = new JTextArea();
		}
		return txtResults;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				BenchmarkConstrutorGenerico thisClass = new BenchmarkConstrutorGenerico();
				thisClass.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				thisClass.setVisible(true);
			}
		});
	}

	/**
	 * This is the default constructor
	 */
	public BenchmarkConstrutorGenerico() {
		super();
		initialize();
	}

	/**
	 * This method initializes this
	 * 
	 * @return void
	 */
	private void initialize() {
		this.setSize(400, 300);
		this.setContentPane(getJContentPane());
		this.setTitle("Benchmark Construtor Genérico");
	}

	/**
	 * This method initializes jContentPane
	 * 
	 * @return javax.swing.JPanel
	 */
	private JPanel getJContentPane() {
		if (jContentPane == null) {
			lblNumeroIteracoes = new JLabel();
			lblNumeroIteracoes.setText("Número de Iterações");
			jContentPane = new JPanel();
			jContentPane.setLayout(new MigLayout("","[][grow]","[][grow, fill][]"));
			jContentPane.add(lblNumeroIteracoes);
			jContentPane.add(getTxtNumeroDeIteracoes(), "width 100, wrap");
			jContentPane.add(getScpResults(), "span, growx, growy, wrap");
			jContentPane.add(getBtnStart(), "span, center");
		}
		return jContentPane;
	}
}
package guj;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

class Sample {
	public Sample (String s1, Integer i1, Double d1) {
		this.s1 = s1; this.i1 = i1; this.d1 = d1;
	}
	public Sample (String s1, int i1, double d1) {
		this.s1 = s1; this.i1 = i1; this.d1 = d1;
	}
	private String s1; private int i1; private double d1;
	public static Sample getInstance (String s1, int i1, double d1) {
		return new Sample (s1, i1, d1);
	}
}

public class Bench {
	
	public Bench (long iterations, JTextArea report) {
		this.iterations = iterations;
		this.report = report;
	}

	private void println (final String msg) {
		SwingUtilities.invokeLater(new Runnable () {
			public void run () { 
				report.append (msg + "\n");
			}
		});
	}
	
	public void test() {
		report.setText("");
		(new Thread(new Runnable() {
			public void run() {
				println ("Iniciando o teste...");
				println ("Executando o aquecimento (necessário para a compilação):");
				for (int i = 0; i < 1000; ++i) testGetInstance (100);
				for (int i = 0; i < 1000; ++i) testGeneric (100);
				for (int i = 0; i < 1000; ++i) testGeneric2 (100);
				println ("Agora fazendo o teste.");
				long ms = System.currentTimeMillis();
				testGetInstance (iterations);
				ms = System.currentTimeMillis() - ms;
				println ("GetInstance levou " + ms + " ms");
				ms = System.currentTimeMillis();
				testGeneric (iterations);
				ms = System.currentTimeMillis() - ms;
				println ("generic levou " + ms + " ms");
				ms = System.currentTimeMillis();
				testGeneric2 (iterations);
				ms = System.currentTimeMillis() - ms;
				println ("generic2 levou " + ms + " ms");
			}
		})).start();
	}

	/** 
	 * Note que este método só pode ser usado se NENHUM dos parâmetros do
	 * construtor for um tipo primitivo.  Se algum deles for, então será
	 * necessário usar o método genericCreate2 abaixo. 
	 * @param <T> A classe
	 * @param klass Um objeto java.lang.Class representando a classe
	 * @param obj Os parâmetros para o construtor
	 * @return O objeto construído. Retornará "null" se houver algum problema.
	 */
	public static<T> T genericCreate (Class<T> klass, Object... obj) {
		Class<?>[] parameterTypes = new Class<?>[obj.length];
		for (int i = 0; i < obj.length; ++i) { parameterTypes[i] = obj[i].getClass(); } 
		try {
			Constructor<T> cons = klass.getConstructor(parameterTypes);
			if (cons != null) {
				T newObj = cons.newInstance(obj);
				return newObj;
			}
		} catch (SecurityException e) {
		} catch (NoSuchMethodException e) {
		} catch (IllegalArgumentException e) {
		} catch (InstantiationException e) {
		} catch (IllegalAccessException e) {
		} catch (InvocationTargetException e) {
		}
		return null;
	}

	/** 
	 * Este método pode ser usado se algum dos parâmetros do construtor desejado
	 * for um tipo primitivo. 
	 * @param <T> A classe
	 * @param parameterTypes os tipos dos parâmetros do construtor desejado.
	 * @param klass Um objeto java.lang.Class representando a classe
	 * @param obj Os parâmetros para o construtor
	 * @return O objeto construído. Retornará "null" se houver algum problema.
	 */
	public static<T> T genericCreate2 (Class<T> klass, Class<?>[] parameterTypes, Object... obj) {
		try {
			Constructor<T> cons = klass.getConstructor(parameterTypes);
			if (cons != null) {
				T newObj = cons.newInstance(obj);
				return newObj;
			}
		} catch (SecurityException e) {
		} catch (NoSuchMethodException e) {
		} catch (IllegalArgumentException e) {
		} catch (InstantiationException e) {
		} catch (IllegalAccessException e) {
		} catch (InvocationTargetException e) {
		}
		return null;
	}

	/**
	 * Caso simples, em que o construtor não tem parâmetros.
	 * @param <T>
	 * @param klass
	 * @return
	 */
	public static<T> T genericCreate3 (Class<T> klass) {
		try {
			T newObj = klass.newInstance();
			return newObj;
		} catch (SecurityException e) {
		} catch (IllegalArgumentException e) {
		} catch (InstantiationException e) {
		} catch (IllegalAccessException e) {
		}
		return null;
	}
	
	
	public void testGetInstance (long iterations) {
		for (long i = 0; i < iterations; ++i) {
			Sample s = Sample.getInstance("abc", 100, Math.PI);
			assert (s != null);
		}
	}
	public void testGeneric (long iterations) {
		for (long i = 0; i < iterations; ++i) {
			Sample s = genericCreate (Sample.class, 
				"abc", 100, Math.PI);
		}
	}
	public void testGeneric2 (long iterations) {
		for (long i = 0; i < iterations; ++i) {
			Sample s = genericCreate2 (Sample.class, 
				new Class<?>[]{String.class, int.class, double.class}, 
				"abc", 100, Math.PI);
		}
	}
	private long iterations;
	private JTextArea report;
}

Rodando este programa, podemos ver que usar um construtor genérico é MUITO mais lento. Por exemplo, para 10 milhões de iterações:

Iniciando o teste…
Executando o aquecimento (necessário para a compilação):
Agora fazendo o teste.
GetInstance levou 93 ms
generic levou 14516 ms
generic2 levou 16516 ms

Este warning do PMD é exatamente para otimização de código pois a criação e o trabalho do GC para liberar a memória é muito grande.

Criando objetos por reflection deixará a criação destes objetos mais lenta ainda e ainda gerará muitos objetos na memória, isto vai apenas piorar a performance.

Um uso correto do warning seria verificar se no código estas instancias não estão sendo criadas distintas vezes em repetições distintas, e otimizar o código para isto.

Para o uso do PMD ser requisito não funcional deve ter algum responsável que entenderia que este problema poderia ser corrigido de forma a gerar mais problemas ainda. Também tente negociar os warnigs… o PMD é apenas uma ferramenta genérica e você não deve usar como regra…

segue um link interessante:

http://pmd.sourceforge.net/bestpractices.html

E se no método getInstance você tratar as excecões e disparar uma exceção do tipo RuntimeException especifica do seu projeto, aí no ponto onde você chama o getInstance, você trata essa exceção especifica?
Desta forma não precisa mexer nas outras camadas.
Isso ajuda?

Abraços!

trecho recortado do link enviado na mensagem anterior:

"
Best Practices
Choose the rules that are right for you

Running every ruleset will result in a huge number of rule violations, most of which will be unimportant. Having to sort through a thousand line report to find the few you’re really interested in takes all the fun out of things.

Instead, start with some of the obvious rulesets - just run unusedcode and fix any unused locals and fields. Then, run basic and fix all the empty if statements and such-like. Then peruse the design and controversial rulesets and use the ones you like via a custom ruleset.
PMD rules are not set in stone

Generally, pick the ones you like, and ignore or suppress the warnings you don’t like. It’s just a tool.
"

Realmente, usar reflection não parecer ser uma boa idéia.
Mas usando ou não reflection use Generics nesse seu método, para ter um controle sobre tipo em tempo de compilação, evitando bastante ClassCastExceptions ai…

Mas nos diga, para o que servirão esses objetos instanciados em loop? Eles serão únicos ou podem ser reaproveitados?
Dependendo da resposta acima caberia muito bem utilizar um FlyWeight.

Abraços.

Bom, se ninguém reparou ainda, os métodos postados (genericCreate, genericCreate2 e genericCreate3) devem satisfazer a exigência do tal líder de projeto, em termos de satisfazer o tal do PMD. Entretanto, gostaria de saber se ele está usando Java 5 ou Java 1.4 (acho que já ouvi falar de um lugar onde você não somente tinha de satisfazer essas exigências estúpidas do PMD, como também era obrigado a usar essa versão do tempo dos dinossauros do Java.) Se não estiver usando Java 5 ou superior, então o código é um pouco mais chato de usar, já que é necessário fazer um cast.