Invocando método protected via Reflection [RESOLVIDO]

Buenas, pessoal!

Já utilizei várias vezes reflection pra invocar métodos de uma classe dinamicamente, porém nao tinha reparado que nao consigo invocar um método protected, mesmo que a classe que o invoca estiver respeitando as regras do modificador de acesso.

Um exemplo do que ocorreu:
Eu tenho vários frames diferentes que disparam um listener em comum (SimpleBeanListener), porém esses frames são herança direta de JFrame e nao possuem relação entre si. Então para invocar cada frame separadamente e fazê-lo disparar o listener usando reflection eu faço da seguinte forma:

Class<? extends JFrame> frameBeanClass = (Class<? extends JFrame>)Class.forName("client.view.JF"+beanClass.getSimpleName()); JFrame frame = frameBeanClass.newInstance(); Method method = frameBeanClass.getMethod("addSimpleBeanListener", SimpleBeanListener.class); method.invoke(frame, new SimpleBeanAdapter() { ... });
O detalhe é: o método addSimpleBeanListener é protected nessas classes. Levando em conta que estou respeitando as regras, como falado anteriormente, deveria ocorrer NoSuchMethodException neste caso?

Amigo antes de dar o invoke faça o seguinte

method.setAccessible(true);

cara, na verdade não adianta pq o erro acontece na linha da criação do objeto Method e não no invoke:

O erro acontece aqui:

Method method = frameBeanClass.getMethod("addSimpleBeanListener", SimpleBeanListener.class);

use

frameBeanClass.getDeclaredMethod

ao invés de

frameBeanClass.getMethod

obrigado pela ajuda…

mas ainda não serve, o getDeclaredMethod vai lançar NoSuchMethodException se o método não estiver explicitamente declarado na classe refletida, e em alguns casos o método invocado está na classe pai…

O correto, já que esses JFrames não têm relação de herança, é fazê-los implementar uma interface que contém esse método. Fazer isso que você quer “desse jeito” até funciona mas é um pouco arriscado, por sinal.

é uma saída também…

eu só tenho que fazer isso pq eu tenho 2 peculiaridades para tratar de forma diferente e eu estou fazendo tudo via reflection e generics. No meu caso, eu tenho 7 frames que gravam cada um em suas respectivas tabelas através de web service. Dentro de cada um deles, tem uma chamada pra outro frame (cada um chama um frame diferente), 5 desses frames herdam de JFSimpleBean (herança de JFrame) e outros 2 herdam diretamente de JFrame… e o que eles tem em comum é a implementação do método que dispara o SimpleBeanListener…
É até confuso de explicar… mas se eu criar uma interface com os métodos que disparam o listener e fazer os frames implementarem, eu usaria a própria interface pra fazer a chamada, mas o que acontece é que eu preciso de um objeto que herde JFrame para fazer algumas operações nessa classe…

não sei se ficou claro

obrigado por responderem…

Uma classe (não objeto) pode estender uma única classe mas pode implementar N interfaces. Por exemplo:

public class CadastroFuncionariosJFrame extends JFrame implements HasSimpleBeanListener {
    ...
}

Evite usar reflection para fazer coisas que podem ser resolvidas com interfaces simples. É muito mais rápido chamar o método diretamente, em vez de usar reflection a torto e a direito.

sim, eu entendo…

o único detalhe é que se eu utilizar interface nesse caso, eu terei que criar uma classe que estará logo acima de JFrame e entao os outros frames ao invés de herdarem JFrame, herdem esta classe (que implementa a interface), pois assim eu conseguiria um objeto como instância de JFrame nesta classe…

um outro detalhe: eu não sei se é uma prática errada, mas quando eu trabalho com generics, uma coisa que eu faço bastante para saber com qual classe estou mechendo é passar junto o class. Na maioria das vezes eu preciso principalmente para pegar o nome da classe e assim buscar as sqls correspondentes etc… Isso é viável ou a melhor alternativa seria algo como um getName em uma interface que essas classes implementariam?

valeu…

Implementar uma interface não quer dizer herdar a implementação. Você está confundindo as coisas. Você pode simplesmente continuar a derivar diretamente de JFrame, implementar a interface HasSimpleBeanListener (por exemplo), e repetir a implementação para cada uma dessas classes.

Não está errado; na verdade não dá para fazer de outra forma. Isso está claramente indicado no tutorial de Generics do Gilad Bracha - ele indica que se deve sempre passar o .class

eu sei… eu acho que eu não to explicando direito hehe…

o fato é que, na hora de instanciar o JFrame, eu não sei qual eu estou chamando. Por isso preciso disso:

JFrame frame = frameBeanClass.newInstance();

pois esse JFrame pode ser um JFCliente ou um JFFuncionario ou um JFBanco, por exemplo…

pra então fazer:

frame.setQualquerCoisaEmJFrame(...);

no meu caso eu já possuo o JFCliente, JFFuncionario, JFBanco, etc implementando a interface que contém os métodos add/remove/getSimpleBeanListener, só que quando eu tenho um objeto Cliente (camada de modelo), eu preciso instanciar um JFCliente (camada de visualização), quando tenho um Funcionario, JFFuncionario e assim sucessivamente… preciso fazer tanto operações de JFrame (sendo que nao sei qual estou intanciando) quanto operações do listener… por isso faço esses invokes dinâmicos com reflection.

Ou, como eu disse antes, pra mim não usar reflection nesse caso acho que o único jeito seria criar uma classe que herde JFrame e implemente HasSimpleBeanListener e então todos os outros frames herdarem essa classe…

Ou eu não estou enxergando o jeito certo, nesse caso kk…

Boa noite pessoal,

Fiz um teste com duas classes, Teste e Teste1

public class Teste {
	
	protected void meuMetodo(){
		System.out.println("Acessou o método protegido");
	}
}
public class Teste1 extends Teste{
	
}

Depois criei a classe que le esse método via Reflection

public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
	
		Class classe = Teste1.class;
		Object classeObject = classe.newInstance();
	
		
		for (Method metodos : classeObject.getClass().getSuperclass().getDeclaredMethods()){
			if (metodos.getName().equals("meuMetodo")){
				metodos.invoke(classeObject);
			}
		}
	}

no final ele apareceu: Acessou o método protegido

Também da para acessar direto sem percorrer todos os métodos:

public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, NoSuchMethodException {
	
		Class classe = Teste1.class;
		Object classeObject = classe.newInstance();
		classeObject.getClass().getSuperclass().getDeclaredMethod("meuMetodo").invoke(classeObject);
	}

Não sei se é bem isso que você precisa, mas o método protected foi acessado…

abraços.

na verdade não…

vc só consegue invocar pq vc chama o método declarado da superclasse, pq vc já sabe que ele está lá… e no meu caso eu nao sei se o método está na classe mãe ou na própria classe…

experimente trocar a linha:

classeObject.getClass().getSuperclass().getDeclaredMethod("meuMetodo")

por

classeObject.getClass().getDeclaredMethod("meuMetodo")

viu?

O correto é fazer assim. Digamos que você tenha uma interface:

interface HasSimpleBeanListener {
    public void addSimpleBeanListener (SimpleBeanListener sbl); 
}

e duas classes que estendem JFrame e implementam HasSimpleBeanListener:

public class CadastroClientesJFrame extends JFrame implements HasSimpleBeanListener {
....
}
public class ListagemProdutosJFrame extends JFrame implements HasSimpleBeanListener {
....
}

Uma vez que você instanciou uma dessas duas classes:

JFrame meuFrame = MinhaFactoryDeClasses.getInstance ("CadastroClientes"); // este método getInstance deve retornar um objeto do tipo JFrame

para você chamar o método addSimpleBeanListener basta fazer:

if (meuFrame instanceof HasSimpleBeanListener) {
     ((HasSimpleBeanListener) meuFrame).addSimpleBeanListener (...);
}

Viu? Não precisa fazer mágica com reflection. Só peço para não ficar usando “protected” quando não é o caso.

e essa Factory de classes retornaria o JFrame de acordo com a String?

public class MinhaFactoryDeClasses { public static JFrame getInstance(String arg) { if (arg.equals("Cliente")) return new JFCliente(); if (arg.equals("Fornecedor")) return new JFFornecedor(); } }
seria algo como isso?

mas já que eu tenho o class do frame eu nao posso instaniá-lo a partir de um newInstance no class? Isso é errado?

já que eu tenho o class do JFrame que eu preciso isntanciar eu nao poderia fazer isso aqui?

JFrame frame = frameBeanClass.newInstance(); if (frame instanceof HasSimpleBeanListener) ((HasSimpleBeanListener)frame).addSimpleBeanListener(...);
me corrija se eu estiver errado…

[quote=erico_kl]já que eu tenho o class do JFrame que eu preciso isntanciar eu nao poderia fazer isso aqui?

JFrame frame = frameBeanClass.newInstance(); if (frame instanceof HasSimpleBeanListener) ((HasSimpleBeanListener)frame).addSimpleBeanListener(...);
me corrija se eu estiver errado…[/quote]

Sim, você pode.

Veja:

[code]public class Test {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Test t = Teste2.class.newInstance();
if(t instanceof Teste2) {
((Teste2) t).print();
}
}
}

class Teste2 extends Test {

protected void print() {
	System.out.println("olá mundo");
}

}[/code]

Perfeitamente aceitavél, pois Teste2 é filho de Test. O contrário já não seria possível.

certo…

valeu, pessoal!