[RESOLVIDO] Como saber a classe de um objeto null? [onde n podemos chamar getClass()]
30 respostas
R
RafaelVS
Pessoal, tem como saber a classe de um objeto mesmo se ele for null?
Sei que em Object tem o método getClass(), mas se eu chamá-lo a partir de um objeto null dá NullPointerException. Existe algum método estático em alguma classe que faça isso?
e tbm… nao tem como saber a classe de um objeto q nao existe certo
R
RafaelVS
Conheco e uso algumas coisas ja de Reflection, mas com isso é possível eu saber a classe de um objeto? achei que a idéira de Reflection fosse operar em cima de Classes, para conhecermos em tempo de execução a estrutura da classe, se herda ou implementa alguem e quem, seus atributos incluindo tipo e talvez modificadores, seus métodos, incluindo tipos de argumento, tipo de retorno, excecoes, se tem anottations, etc.
Mas com Reflection dá pra operar em cima de um objeto? em especial um objeto null?
O instanceof não funciona porque eu preciso saber qual a classe do objeto independente de qual seja… Não tenho um subconjunto de classes pra testar o tipo do objeto…
Se eu fosse usar o instanceof teria um zilhões de if/else’s e pra cada classe nova existente eu teria que colocar mais um if/else.
Mas valeu pela força!!
Zakim
o reflection serve para vc saber de onde o objeto vem, quais seu metodos e variaveis entre outros.
isso ajuda no reconhecimento automatizado de um objeto e seu tipo…
Aldrin_Leal
Rafael,
Você pode nos ajudar e dar mais detalhes sobre o que, afinal de contas, queres fazer?
R
RafaelVS
Oi, o que eu quero é generalizar uma chamada a um método através de Reflection.
Quero criar um método que recebe o objeto a invocar um método, o nome do método a ser invocado e apenas os argumentos. Usando Reflection, pra fazermos isso, precisamos pegar o método e invocá-lo, com o código abaixo:
Esse foi mais ou menos o código que eu coloquei pra chamar um método por Reflection, pois vários pontos da aplicação precisam desse código e resolvi centralizá-lo.
Só que, pra simplificar a chamada a tal método, gostaria de saber se tem como a partir de um objeto saber a classe dele para eu poder trocar a assinatura, retirando o parâmetro argumentoClasses e pegando o valor das classes dentro do próprio método, através do parâmetro argumentoObjetos.
Inicialmente eu estava usando objeto.getClass(), só que tem casos em que um objeto é null e, nesses casos, dá NullPointerException. Portanto, gostaria de saber se dado um objeto tem alguma forma de saber a classe dele, mesmo que ele seja null.
SmartCardMan
RafaelVC:
Tecnicamente creio que seja impossível vc conseguir o class name de um objeto não instanciado, ate porque ele não existe e portanto não há informação sobre ele que possa ser recuperada por reflection. Creio que a unica solução seria ou vc desconsiderar objetos nulos ou quem sabe talvez, fazer uma análise lexica do codigo e capturar os caracteres antes do nome do objeto.
A não ser que haja alguma API maluca que faça isso… a que chega mais perto que conheço eh o commons-lang da apache… mas ficaria algo como:
ClassUtils.getShortClassName(obj,"NULO");
Ele daria o nome se houvesse instância, e no caso de ser nulo ele retorna a string.
Quem souber de alguma API que faça o trabalho por gentileza me comunique!!!
gomesrod
Eu acho que você não vai conseguir fazer isso não....
Se alguem com uma explicação teórica mais aprofundada puder me corrigir/completar fique à vontade, mas o que ocorre basicamente é que não existe um "null diferente para cada classe".
Por exemplo, faça o teste abaixo:
Strings=null;if(sinstanceofString){System.out.println("s é String");}else{// Vai entrar aquiSystem.out.println("s NÃO é String.");}
Você verá que não importa como a variável foi declarada, ela aponta para "nada" e esse "nada" não é objeto de nenhuma classe.
sergiotaborda
Mas um NullPointerException é exactamente o que deve dar.
Vc pode testar se o objeto é nulo e lançar outra exceção, mas é isso ai.
Mesmo que vc conseguisse obter a classe vc iria precisar de um objeto não nulo na API de reflection.
SmartCardMan
gomesrod:
Eu acho que você não vai conseguir fazer isso não....
Se alguem com uma explicação teórica mais aprofundada puder me corrigir/completar fique à vontade, mas o que ocorre basicamente é que não existe um "null diferente para cada classe".
Por exemplo, faça o teste abaixo:
Strings=null;if(sinstanceofString){System.out.println("s é String");}else{// Vai entrar aquiSystem.out.println("s NÃO é String.");}
Você verá que não importa como a variável foi declarada, ela aponta para "nada" e esse "nada" não é objeto de nenhuma classe.
É exatamente isso!
Não existe objetos null especificos para cara objeto.
Não há instância não há informação sobre o objeto, ate porque ele nao existe.
E mesmo que vc ocnseguisse o nome da classe, vc nao poderia executar os metodos dele ja que daria NullPointerException de qualquer forma.
R
RafaelVS
Como Java é fortemente tipada, eu achei que talvez tivesse alguma forma de Java saber isso buscando, em último caso, o tipo da variável. Porque isso ele conhece em tempo de compilação e, portanto, achei que tivesse uma forma de conhecer isso tb em tempo de execução. Algo como se o objeto for diferente de null, retorne o tipo do objeto que foi instanciado e se for igual a null retorne o tipo da variavel.
sergiotaborda:
RafaelVS:
Inicialmente eu estava usando objeto.getClass(), só que tem casos em que um objeto é null e, nesses casos, dá NullPointerException. Portanto, gostaria de saber se dado um objeto tem alguma forma de saber a classe dele, mesmo que ele seja null.
Mas um NullPointerException é exactamente o que deve dar.
Vc pode testar se o objeto é nulo e lançar outra exceção, mas é isso ai.
Mesmo que vc conseguisse obter a classe vc iria precisar de um objeto não nulo na API de reflection.
Usando o código que eu postei anteriormente eu consigo chamar métodos através de Reflection mesmo passando os argumentos nulos.
O meuMetodo será invocado onde os parâmetros terão os valores: a=“a” e b=null.
Só que o que eu queria era retirar isso do meu código:
Class<?>[]{String.class, String.class} ( o 3. argumento do método invocaMetodo)
Dado que os tipos de parametro1 e parametro2 são String, gostaria, se possível, de uma forma que eu conseguisse as classes dos parametros.
sergiotaborda
Eu não disse “argumentos nulos” eu disse “objeto nulo”
A API de reflection como o proprio nome indica reflete sobre o objeto
Ora se não ha nenhum objeto, ela vai refletir no quê ?
(na realidade sem objeto ela pode refletir na classe e invocar metodos estáticos, mas não o caso aqui)
É ilogico invocar um método sobre uma variável null, mesmo que seja por reflection. Por isso a exceção NullPointerException é a exceção correta.
Enfim, vc não deve invocar a sua API sobre variáveis nulas.
Sami_Koivu
RafaelVS:
Como Java é fortemente tipada, eu achei que talvez tivesse alguma forma de Java saber isso buscando, em último caso, o tipo da variável. Porque isso ele conhece em tempo de compilação e, portanto, achei que tivesse uma forma de conhecer isso tb em tempo de execução. Algo como se o objeto for diferente de null, retorne o tipo do objeto que foi instanciado e se for igual a null retorne o tipo da variavel.
Os tipos de variáveis estão presentes para fins de debug opcionalmente, dependendo de opções de compilação. Nem sempre.
Obter essa informação (quando presente) é possível, mas trabalhoso.
Você ainda precisaria de mapear seu null a um variavel. E isso, para fins práticas, acho impossível. Pode ser que não há nenhum variavel. Pode ser que alguém esteja chamando seu método passando o null literal, etc.
R
RafaelVS
sergiotaborda:
RafaelVS:
Usando o código que eu postei anteriormente eu consigo chamar métodos através de Reflection mesmo passando os argumentos nulos.
Eu não disse “argumentos nulos” eu disse “objeto nulo”
A API de reflection como o proprio nome indica reflete sobre o objeto
Ora se não ha nenhum objeto, ela vai refletir no quê ?
(na realidade sem objeto ela pode refletir na classe e invocar metodos estáticos, mas não o caso aqui)
É ilogico invocar um método sobre uma variável null, mesmo que seja por reflection. Por isso a exceção NullPointerException é a exceção correta.
Enfim, vc não deve invocar a sua API sobre variáveis nulas.
Entendi, mas foi por isso que em umas respostas anteriores eu disse que gostaria de saber se não existe alguma outra classe com um método estático que receba a variável e retorne seu tipo, seja o do objeto ou o tipo da variável.
E, em relação ao que Sami Koivu falou, no caso do desenvolvedor passar a literal null aí realmente não tem como saber seu tipo, mas como o que eu estou fazendo é para eu mesmo utilizar, eu não passaria nunca o literal null e não precisaria de controle sobre isso. Caso eu por acaso passe a literal null sem querer, pode estourar erro porque seria erro de programação mesmo.
Aldrin_Leal
RafaelVS:
Oi, o que eu quero é generalizar uma chamada a um método através de Reflection.
Quero criar um método que recebe o objeto a invocar um método, o nome do método a ser invocado e apenas os argumentos. Usando Reflection, pra fazermos isso, precisamos pegar o método e invocá-lo, com o código abaixo:
Putz… pelo que tou vendo, o método que escrevi, invocaMetodo que vc citou, faz a mesma coisa que o que vc escreveu, só que é mais simples de usar.
para usar o método que escrevi é só uma chamada ao método:
a) recebe o objeto, nome do metodo, classes dos argumentos, argumentos
b) pega o método e o invoca.
para usar o método que vc sugeriu preciso instanciar um objeto e chamar um método:
a) constrói um objeto InvokeAdapter passando o nome da classe, nome do metodo e as classes dos argumentos
b) invoca o método passando a instancia e os argumentos.
No final das contas o resultaodo é o mesmo e ainda não resolve o problema que estou tentando, que é evitar passar Class<?>[] argumentos.
Mas obrigado pela ajuda, Aldrin!
Aldrin_Leal
RafaelVS:
Putz… pelo que tou vendo, o método que escrevi, invocaMetodo que vc citou, faz a mesma coisa que o que vc escreveu, só que é mais simples de usar.
Simples?
H. L. Mencken:
There is always an easy solution to every human problem - neat, plausible and wrong.
Não sei se a sua definição de simples bate com a minha. Na verdade, acho que bate com a que o H. L. Mencken cita. Mas enfim, vou ignorar esta Thread a partir de agora, por motivos de ignorância da minha parte :).
Você esqueceu o detalhe sutil da coisa: O que define o método é, na ordem, é:
Classe
Nome
Tipos do Argumento (<- E ISTO ENVOLVE O Class<?>[])
A idéia do InvokeAdapter é apenas usar o mínimo possível da Reflection. E quando o fizê-lo, não ser por Object.getClass(), e sim pela definição do InvokeAdapter.
Enfim, boa sorte. Você irá precisar dela
Aldrin_Leal
Isto está contido no .class, independente das flags de compilação. Em debug, somente numeros de linha constam a mais.
Reflection é parte integrante do sistema.
R
RafaelVS
Aldrin Leal:
Você esqueceu o detalhe sutil da coisa: O que define o método é, na ordem, é:
Classe
Nome
Tipos do Argumento (<- E ISTO ENVOLVE O Class<?>[])
A idéia do InvokeAdapter é apenas usar o mínimo possível da Reflection. E quando o fizê-lo, não ser por Object.getClass(), e sim pela definição do InvokeAdapter.
Enfim, boa sorte. Você irá precisar dela :)
Acho que vc não acompanhou a thread direito e nem entendeu a minha necessidade.
Claro que isso que vc informou é o que é necessário pra invocar um método e eu não quero de maneira alguma invocar o método sem no final das contas informar as classes dos argumentos. O que eu quero saber é justamente se é possível não informar diretamente o “Tipos do Argumento (<- E ISTO ENVOLVE O Class<?>[])” que vc citou e aproveitando que eu preciso passar os argumentos ao método, inferir seus tipos usando algum método estático de alguma classe java (se existir).
Acho que vc confundiu o que eu quis dizer em relação à comparação que eu fiz entre o uso dos métodos. Em momento algum tive a intenção de dizer que o método que eu escrevi é melhor que o seu. Mas convenhamos, que diferente do que o tal H. L. Mencken disse, aquela forma de chamar um método que eu escrevi acaba sendo mais simples e não está errada.
Em termos de código, o que eu quero saber é se é possível fazer algo como isso:
Ou seja, eu estaria abstraindo de quem vai chamar o método invocaMetodo a necessidade de informar as classes dos argumentos, mas internamente, mais uma vez se existisse o METODO_MAGICO_DE_API_MAGICA (que é a única resposta de que estou precisando nessa thread), na chamada à API Reflection eu estaria informando sim as classes dos argumentos.
R
RafaelVS
Depois que vi o código que postei acima, percebi que acho que não seja possível, mas não por algumas justificativas que li por aí, mas por causa do seguinte:
o que eu estava querendo era um METODO_MAGICO_DE_API_MAGICA(objeto); que fizesse o seguinte:
se o objeto tiver instancia, entao retorna o tipo da instancia.
se o objeto for null, entao retorna o tipo da variável.
Porém, se vc for imaginar como seria o corpo desse método, teríamos o seguinte:
Para atender a todos os casos, ele teria que ser ± assim (atenção na assinatura):
publicstaticClass<?>METODO_MAGICO_DE_API_MAGICA(Objectobject){if(object!=null){returnobject.getClass();}else{return//faz a mágina com object pra retornar o tipo da variável object.}}
Porém, dentro desse método, o tipo da variável object SEMPRE vai ser Object. Por esse motivo, acho que é impossível obter o tipo da variável que foi passada na chamada ao método, que era o que eu estava querendo.
Obrigado a todos e peço desculpas aos que possam ter ficado chateados com minha insistência. Mas foi porque eu não consegui nenhuma explicação plausível e não sossegaria até consegui-la.
SmartCardMan
RafaelVS:
Depois que vi o código que postei acima, percebi que acho que não seja possível, mas não por algumas justificativas que li por aí, mas por causa do seguinte:
o que eu estava querendo era um METODO_MAGICO_DE_API_MAGICA(objeto); que fizesse o seguinte:
se o objeto tiver instancia, entao retorna o tipo da instancia.
se o objeto for null, entao retorna o tipo da variável.
Porém, se vc for imaginar como seria o corpo desse método, teríamos o seguinte:
Para atender a todos os casos, ele teria que ser ± assim (atenção na assinatura):
publicstaticClass<?>METODO_MAGICO_DE_API_MAGICA(Objectobject){if(object!=null){returnobject.getClass();}else{return//faz a mágina com object pra retornar o tipo da variável object.}}
Porém, dentro desse método, o tipo da variável object SEMPRE vai ser Object. Por esse motivo, acho que é impossível obter o tipo da variável que foi passada na chamada ao método, que era o que eu estava querendo.
Obrigado a todos e peço desculpas aos que possam ter ficado chateados com minha insistência. Mas foi porque eu não consegui nenhuma explicação plausível e não sossegaria até consegui-la.
Ok, mas so uma dúvida minha agora…
mesmo que vc conseguisse descobrir o tipo da variavel, no caso de ser null, de que lhe adiantaria se ainda assim vc nao poderia invocar nenhum metodo do objeto ? (NullPointerException)
R
RafaelVS
SmartCardMan:
Ok, mas so uma dúvida minha agora…
mesmo que vc conseguisse descobrir o tipo da variavel, no caso de ser null, de que lhe adiantaria se ainda assim vc nao poderia invocar nenhum metodo do objeto ? (NullPointerException)
Mas eu não queria descobrir o tipo da variável para chamar um método dessa variável. Eu queria saber o tipo da variável para eu poder informar ao método Class.getMethod(Class<?>[] tipoArgumentos, Object[] objetos); Esse método vai procurar na minha classe um método com a assinatura informada. Não vai invocá-lo. Tendo o metodo retornado, eu o invocaria passando um objeto não nulo sempre, podendo passar argumentos nulos, o que é normal em uma chamada de método.
Em códigos:
publicclassMinhaClasse{publicObjectmeuMetodo(Stringparametro1,Stringparametro2){Objectretorno=null;if(parametro2==null){//legal, não faça nada com parametro2.}else{//bom, então vamos usar parametro1.}//faça algo com parametro1.//ajuste o retorno com um tempero especial.returnretorno;}}//em outra classe, simplificando o código, a chamada seria:publicObjectinvocaMetodoComReflection(){Stringargumento1="valor";Stringargumento2=null;//Note que eu preciso informar no segundo argumento os tipos das classes //dos objetos que eu vou passar na hora de invocar.Methodmetodo=MinhaClasse.getMethod("meuMetodo",newClass<?>[]{String.class,String.class});//Aqui eu estou informando invocando //passando uma instancia(ou seja, objeto não null) e os //argumento1 e argumento2 (que é null)returnmetodo.invoke(newMinhaClasse(),newObject[]{argumento1,argumento2});}//Se eu pudesse garantir que na minha aplicação //os objetos passados nunca seriam null, eu poderia //centralizar as chamadas a API Reflection assim e //vejam na assinatura do método que não preciso //informar os tipos dos argumentos, o que simplificaria //ainda mais a chamada:publicObjectinvocaMetodoSemArgumentosNull(Objectobjeto,StringnomeMetodo,Object[]argumentoObjetos)throwsException{Class<?>[]argumentoClasses=newClass<?>[argumentoObjetos.length];for(inti=0;i<argumentoObjetos.length;i++){//Se tivesse argumento null aqui daria NullPointerExceptionargumentoClasses[i]=argumentoObjetos[i].getClass();}Methodmetodo=objeto.getClass().getMethod(nomeMetodo,argumentosClasses);returnmetodo.invoke(action,argumentosObjetos);}//Porém, já que não posso inferir o tipo da variável //quando os argumentos são null, se algum argumento //a ser passado para o método a ser invocado tem a //possibilidade de ser null, então é preciso chamar //usando esse método, ou seja, informando os //tipos dos argumentos.publicObjectinvocaMetodoComEventuaisArgumentosNull(Objectobjeto,StringnomeMetodo,Class<?>[]argumentoClasses,Object[]argumentoObjetos)throwsException{Methodmetodo=objeto.getClass().getMethod(nomeMetodo,argumentosClasses);returnmetodo.invoke(objeto,argumentosObjetos);}//A chamada então para invocação de métodos ficaria assim:publicstaticvoidmain(String...args){MinhaClasseobjeto=newMinhaClasse();StringnomeMetodo="meuMetodo";Stringargumento1="valor";Stringargumento2="valor";Stringargumento3=nul;//Prepara chamada1:Object[]argumentosChamada1=newObject[]{argumento1,argumento2};//Agora a chamada, mais simples, não precisa de Class<?>[]. E era essa chamada//que eu queria que fosse feita, mesmo se os argumentos pudessem//ser null, mas isso só seria possível se, de alguma forma, dentro de//invocaMetodoSemArgumentosNull() eu pudesse descobrir o tipo da//variável que eu passei, mesmo quando essa fosse null. Mas o objeto//que invocaria a chamada não é null, pois esse é o argumento "objeto"//que estou passando na chamada ao método, como vemos abaixo:invocaMetodoSemArgumentosNull(objeto,nomeMetodo,argumentosChamada1);//Prepara chamada2:Class<?>[]tiposArgumentosChamada2=newClass<?>[]{String.class,String.class};//Menos simples, precisa de Class<?>[], mas ainda assim é mais simples que diretamente à API Reflection.Object[]argumentosChamada2=newObject[]{argumento2,argumento3};invocaMetodoComEventuaisArgumentosNull(objeto,nomeMetodo,tiposArgumentosChamada2,argumentosChamada2);}
Então, no final das contas o que eu queria era uma forma de descobrir o tipo da variável para poder as chamadas serem todas parecidas com a chamada1, onde não preciso informar diretamente o argumento Class<?>[].
ps.: eu escrevi o código acima diretamente daqui do forum, então pode ser que haja algum erro de compilação que tenha passado despercebido.
Sami_Koivu
Aldrin Leal:
Sami Koivu:
Os tipos de variáveis estão presentes para fins de debug opcionalmente, dependendo de opções de compilação. Nem sempre.
Isto está contido no .class, independente das flags de compilação. Em debug, somente numeros de linha constam a mais.
Reflection é parte integrante do sistema.
Olá,
Tipos de variáveis (que são definidos dentro de um bloco de código), diferentemente de campos (atributos) ou métodos de um classe não são disponíveis pela API de Reflection.
E essa informação nem sempre está disponível. Depende de flags de compilação, sim.
Você está certo em que os números de linha também são opcionalmente incluídos.
[]s,
Sami
R
RaphaelSantos
Pessoal, o que é o cmando Class<?>[] ?? quando se usa isso?
e a classe Method é o q??
R
RafaelVS
RaphaelSantos:
Pessoal, o que é o cmando Class<?>[] ?? quando se usa isso?
e a classe Method é o q??
I. Class<?>[] não é um comando. Vou explicar o que é por partes:
Class é uma classe de Java, assim como String ou qualquer outra.
Quando uso Class<?>, a parte <?> quer dizer que é um tipo genérico que herda de Object (vc pode pesquisar melhor sobre tipos genéricos se preferir se aprofundar)
Quando coloco os colchetes, chegando finalmente a Class<?>[], estou me referenciando a um array de do tipo Class<?>, assim como vc declara arrays comuns, como int[].
Na discussão aqui a “expressão” Class<?>[] está sendo muito utilizada porque ela é o parâmetro de um método de uma classe em Java, da API Reflection (vc tb pode pesquisar sobre Reflection se preferir se aprofundar).
II. Method é uma classe da API Reflection e ela representa exatamente o que o nome indica, um método de uma classe java. Todo método que vc escreve nas suas classes podem ser lidos como um objeto do tipo Method. Para entender melhor, sugiro que vc estude a api Reflection.
Espero ter ajudado.
[]'s
Aldrin_Leal
Desculpe, compreendi errado, confundindo com o metadata da classe. Bem, exceto nos casos aonde temos obfuscators, sempre existe o ASM, certo?
R
RaphaelSantos
RafaelVS:
RaphaelSantos:
Pessoal, o que é o cmando Class<?>[] ?? quando se usa isso?
e a classe Method é o q??
I. Class<?>[] não é um comando. Vou explicar o que é por partes:
Class é uma classe de Java, assim como String ou qualquer outra.
Quando uso Class<?>, a parte <?> quer dizer que é um tipo genérico que herda de Object (vc pode pesquisar melhor sobre tipos genéricos se preferir se aprofundar)
Quando coloco os colchetes, chegando finalmente a Class<?>[], estou me referenciando a um array de do tipo Class<?>, assim como vc declara arrays comuns, como int[].
Na discussão aqui a “expressão” Class<?>[] está sendo muito utilizada porque ela é o parâmetro de um método de uma classe em Java, da API Reflection (vc tb pode pesquisar sobre Reflection se preferir se aprofundar).
II. Method é uma classe da API Reflection e ela representa exatamente o que o nome indica, um método de uma classe java. Todo método que vc escreve nas suas classes podem ser lidos como um objeto do tipo Method. Para entender melhor, sugiro que vc estude a api Reflection.
onde posso arrumar informaçoes/tutoriais sobre uso/funcionamento/etc sobre reflection(que nao sejam em ingles)???
R
RafaelVS
Class<?> é uma classe como outra qualquer, tem duas formas (pelo menos) de vc obter uma instancia de Classe<?>:
a partir de um objeto, chamar objeto.getClass();
NomeClasse.class;
Então, no codigo que vc colocou, o metodo retorna Class<?> e quando vc faz return String.classe vc ta retornando uma instancia de Class. A partir dessa instancia, vc pode ver acessar algumas informacoes da classe String, como nomes de metodos e pode ate invoca-los.
Material em português eu não conheco, mas quando eu comecei a estudar Reflection sobre perguntei aqui no GUJ e o VinliGodoy (acho que é esse o nome do usuario) me passou esse link, que foi suficiente pra eu fazer tudo que eu queria e mais um pouco (em ingles ) :