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?
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.
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…
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.
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?
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…
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…
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();
}
}
}