Pergunta

O que é reflexão e por que é útil?

Estou particularmente interessado em Java, mas presumo que os princípios sejam os mesmos em qualquer linguagem.

Foi útil?

Solução

O nome reflexão é usado para descrever código que é capaz de inspecionar outro código no mesmo sistema (ou em si mesmo).

Por exemplo, digamos que você tenha um objeto de tipo desconhecido em Java e gostaria de chamar um método 'doSomething' nele, se existir.O sistema de tipagem estática do Java não foi realmente projetado para suportar isso, a menos que o objeto esteja em conformidade com uma interface conhecida, mas usando reflexão, seu código pode olhar para o objeto e descobrir se ele tem um método chamado 'doSomething' e então chamá-lo se você quer.

Então, para dar um exemplo de código disso em Java (imagine que o objeto em questão seja foo):

Method method = foo.getClass().getMethod("doSomething", null);
method.invoke(foo, null);

Um caso de uso muito comum em Java é o uso com anotações.JUnit 4, por exemplo, usará reflexão para procurar métodos marcados com a anotação @Test em suas classes e, em seguida, os chamará ao executar o teste de unidade.

Existem alguns bons exemplos de reflexão para você começar http://docs.oracle.com/javase/tutorial/reflect/index.html

E finalmente, sim, os conceitos são bastante semelhantes em outras linguagens de tipos estaticamente que suportam reflexão (como C#).Em linguagens de tipo dinâmico, o caso de uso descrito acima é menos necessário (já que o compilador permitirá que qualquer método seja chamado em qualquer objeto, falhando em tempo de execução se ele não existir), mas o segundo caso de procurar métodos marcados ou trabalhar de uma determinada maneira ainda é comum.

Atualização de um comentário:

A capacidade de inspecionar o código no sistema e ver tipos de objetos não é reflexão, mas digite introspecção.A reflexão é, então, o capacidade de fazer modificações em tempo de execução fazendo uso de introspecção.A distinção é necessária aqui como algumas línguas apoiam a introspecção, mas não apoiam a reflexão.Um exemplo é C++

Outras dicas

Reflexão é a capacidade de uma linguagem de inspecionar e chamar dinamicamente classes, métodos, atributos, etc.em tempo de execução.

Por exemplo, todos os objetos em Java possuem o método getClass(), que permite determinar a classe do objeto mesmo que você não saiba em tempo de compilação (por exemplo,se você declarou isso como um Object) - isso pode parecer trivial, mas tal reflexão não é possível em linguagens menos dinâmicas como C++.Usos mais avançados permitem listar e chamar métodos, construtores, etc.

A reflexão é importante porque permite escrever programas que não precisam "saber" tudo em tempo de compilação, tornando-os mais dinâmicos, pois podem ser interligados em tempo de execução.O código pode ser escrito em interfaces conhecidas, mas as classes reais a serem usadas podem ser instanciadas usando reflexão de arquivos de configuração.

Muitas estruturas modernas usam a reflexão extensivamente por esse motivo.A maioria das outras linguagens modernas também usa reflexão, e em linguagens de script (como Python) elas são ainda mais integradas, já que parece mais natural dentro do modelo geral de programação dessas linguagens.

Um dos meus usos favoritos de reflexão é o método de dump Java abaixo.Ele pega qualquer objeto como parâmetro e usa a API de reflexão Java para imprimir cada nome e valor de campo.

import java.lang.reflect.Array;
import java.lang.reflect.Field;

public static String dump(Object o, int callCount) {
    callCount++;
    StringBuffer tabs = new StringBuffer();
    for (int k = 0; k < callCount; k++) {
        tabs.append("\t");
    }
    StringBuffer buffer = new StringBuffer();
    Class oClass = o.getClass();
    if (oClass.isArray()) {
        buffer.append("\n");
        buffer.append(tabs.toString());
        buffer.append("[");
        for (int i = 0; i < Array.getLength(o); i++) {
            if (i < 0)
                buffer.append(",");
            Object value = Array.get(o, i);
            if (value.getClass().isPrimitive() ||
                    value.getClass() == java.lang.Long.class ||
                    value.getClass() == java.lang.String.class ||
                    value.getClass() == java.lang.Integer.class ||
                    value.getClass() == java.lang.Boolean.class
                    ) {
                buffer.append(value);
            } else {
                buffer.append(dump(value, callCount));
            }
        }
        buffer.append(tabs.toString());
        buffer.append("]\n");
    } else {
        buffer.append("\n");
        buffer.append(tabs.toString());
        buffer.append("{\n");
        while (oClass != null) {
            Field[] fields = oClass.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                buffer.append(tabs.toString());
                fields[i].setAccessible(true);
                buffer.append(fields[i].getName());
                buffer.append("=");
                try {
                    Object value = fields[i].get(o);
                    if (value != null) {
                        if (value.getClass().isPrimitive() ||
                                value.getClass() == java.lang.Long.class ||
                                value.getClass() == java.lang.String.class ||
                                value.getClass() == java.lang.Integer.class ||
                                value.getClass() == java.lang.Boolean.class
                                ) {
                            buffer.append(value);
                        } else {
                            buffer.append(dump(value, callCount));
                        }
                    }
                } catch (IllegalAccessException e) {
                    buffer.append(e.getMessage());
                }
                buffer.append("\n");
            }
            oClass = oClass.getSuperclass();
        }
        buffer.append(tabs.toString());
        buffer.append("}\n");
    }
    return buffer.toString();
}

Usos da reflexão

A reflexão é comumente usada por programas que requerem a capacidade de examinar ou modificar o comportamento de tempo de execução de aplicativos em execução na máquina virtual Java.Este é um recurso relativamente avançado e deve ser usado apenas por desenvolvedores que tenham um bom domínio dos fundamentos da linguagem.Com essa ressalva em mente, a reflexão é uma técnica poderosa e pode permitir que aplicativos executem operações que de outra forma seriam impossíveis.

Recursos de extensibilidade

Um aplicativo pode fazer uso de classes externas definidas pelo usuário criando instâncias de objetos de extensibilidade usando seus nomes totalmente qualificados.Navegadores de classe e ambientes de desenvolvimento visual Um navegador de classes precisa ser capaz de enumerar os membros de classes.Os ambientes de desenvolvimento visual podem se beneficiar do uso de informações de tipo disponíveis na reflexão para ajudar o desenvolvedor a escrever o código correto.Depuradores e ferramentas de teste Os depuradores precisam ser capazes de examinar membros particulares nas aulas.Os equipamentos de teste podem fazer uso da reflexão para chamar sistematicamente um conjunto detectável de APIs definidas em uma classe, para garantir um alto nível de cobertura de código em um conjunto de testes.

Desvantagens da reflexão

A reflexão é poderosa, mas não deve ser usada indiscriminadamente.Se for possível realizar uma operação sem utilizar reflexão, então é preferível evitá-la.As seguintes preocupações devem ser lembradas ao acessar o código por meio de reflexão.

  • Sobrecarga de desempenho

Como a reflexão envolve tipos que são resolvidos dinamicamente, determinadas otimizações da Java Virtual Machine não podem ser executadas.Conseqüentemente, as operações reflexivas têm desempenho mais lento do que suas contrapartes não reflexivas e devem ser evitadas em seções de código que são chamadas com frequência em aplicações sensíveis ao desempenho.

  • Restrições de segurança

O Reflection requer uma permissão de tempo de execução que pode não estar presente quando executado sob um gerenciador de segurança.Esta é uma consideração importante para código que precisa ser executado em um contexto de segurança restrito, como em um miniaplicativo.

  • Exposição de Internos

Como a reflexão permite que o código execute operações que seriam ilegais em código não reflexivo, como acessar campos e métodos privados, o uso da reflexão pode resultar em efeitos colaterais inesperados, que podem tornar o código disfuncional e destruir a portabilidade.O código reflexivo quebra abstrações e, portanto, pode mudar o comportamento com atualizações da plataforma.

fonte: A API de reflexão

A reflexão é um mecanismo chave para permitir que um aplicativo ou estrutura funcione com código que talvez ainda não tenha sido escrito!

Tomemos por exemplo o seu típico arquivo web.xml.Isso conterá uma lista de elementos de servlet, que contêm elementos de classe de servlet aninhados.O contêiner do servlet processará o arquivo web.xml e criará uma nova instância de cada classe de servlet por meio de reflexão.

Outro exemplo seria a API Java para análise XML (JAXP).Onde um provedor de analisador XML é 'conectado' por meio de propriedades de sistema conhecidas, que são usadas para construir novas instâncias por meio de reflexão.

E, finalmente, o exemplo mais abrangente é Primavera que usa reflexão para criar seus beans e pelo uso intenso de proxies

Nem todas as línguas suportam a reflexão, mas os princípios são geralmente os mesmos nas línguas que a suportam.

Reflexão é a capacidade de “refletir” sobre a estrutura do seu programa.Ou mais concreto.Para examinar os objetos e classes que você possui e obter programaticamente informações sobre os métodos, campos e interfaces que eles implementam.Você também pode ver coisas como anotações.

É útil em muitas situações.Em qualquer lugar que você queira poder conectar classes dinamicamente ao seu código.Muitos mapeadores relacionais de objetos usam reflexão para poder instanciar objetos de bancos de dados sem saber antecipadamente quais objetos eles usarão.As arquiteturas de plug-in são outro lugar onde a reflexão é útil.Ser capaz de carregar código dinamicamente e determinar se existem tipos que implementam a interface correta para usar como plug-in é importante nessas situações.

A reflexão permite instanciação de novos objetos, invocação de métodos e operações get/set em variáveis ​​de classe dinamicamente em tempo de execução sem ter conhecimento prévio de sua implementação.

Class myObjectClass = MyObject.class;
Method[] method = myObjectClass.getMethods();

//Here the method takes a string parameter if there is no param, put null.
Method method = aClass.getMethod("method_name", String.class); 

Object returnValue = method.invoke(null, "parameter-value1");

No exemplo acima, o parâmetro nulo é o objeto no qual você deseja invocar o método.Se o método for estático, você fornece nulo.Se o método não for estático, ao invocar você precisará fornecer uma instância MyObject válida em vez de nula.

O Reflection também permite que você acesse membros/métodos privados de uma classe:

public class A{

  private String str= null;

  public A(String str) {
  this.str= str;
  }
}

.

A obj= new A("Some value");

Field privateStringField = A.class.getDeclaredField("privateString");

//Turn off access check for this field
privateStringField.setAccessible(true);

String fieldValue = (String) privateStringField.get(obj);
System.out.println("fieldValue = " + fieldValue);
  • Para inspeção de classes (também conhecida como introspecção) você não precisa importar o pacote de reflexão (java.lang.reflect).Os metadados da classe podem ser acessados ​​através java.lang.Class.

Reflection é uma API muito poderosa, mas pode deixar a aplicação lenta se usada em excesso, pois resolve todos os tipos em tempo de execução.

Exemplo :
Tomemos, por exemplo, um aplicativo remoto que fornece ao seu aplicativo um objeto que você obtém usando seus métodos de API.Agora, com base no objeto, talvez você precise realizar algum tipo de cálculo.
O provedor garante que o objeto pode ser de 3 tipos e precisamos realizar o cálculo com base no tipo de objeto.
Portanto, podemos implementar em 3 classes, cada uma contendo uma lógica diferente. Obviamente, as informações do objeto estão disponíveis em tempo de execução, então você não pode codificar estaticamente para realizar o cálculo, portanto, a reflexão é usada para instanciar o objeto da classe que você precisa para realizar o cálculo com base no objeto recebido do provedor.

Java Reflection é bastante poderoso e pode ser muito útil.Java Reflection torna isso possível inspecionar classes, interfaces, campos e métodos em tempo de execução, sem saber os nomes das classes, métodos etc.em tempo de compilação.Também é possível instanciar novos objetos, invocar métodos e obter/definir valores de campo usando reflexão.

Um exemplo rápido de Java Reflection para mostrar como é usar a reflexão:

Method[] methods = MyObject.class.getMethods();

    for(Method method : methods){
        System.out.println("method = " + method.getName());
    }

Este exemplo obtém o objeto Class da classe chamada MyObject.Usando o objeto de classe, o exemplo obtém uma lista dos métodos dessa classe, itera os métodos e imprime seus nomes.

Exatamente como tudo isso funciona é explicado aqui

Editar:Depois de quase 1 ano, estou editando esta resposta, pois ao ler sobre reflexão, consegui mais alguns usos do Reflection.

  • Spring usa configuração de bean como:


<bean id="someID" class="com.example.Foo">
    <property name="someField" value="someValue" />
</bean>

Quando o contexto Spring processa este elemento <bean>, ele usará Class.forName(String) com o argumento "com.example.Foo" para instanciar essa classe.

Ele usará novamente a reflexão para obter o setter apropriado para o elemento <property> e definir seu valor para o valor especificado.

  • Junit usa Reflection especialmente para testar métodos privados/protegidos.

Para métodos privados,

Method method = targetClass.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
return method.invoke(targetObject, argObjects);

Para campos privados,

Field field = targetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);

De acordo com meu entendimento:

A reflexão permite que o programador acesse entidades no programa dinamicamente.ou sejaao codificar um aplicativo, se o programador não tiver conhecimento de uma classe ou de seus métodos, ele poderá fazer uso dessa classe dinamicamente (em tempo de execução) usando reflexão.

É frequentemente usado em cenários onde o nome de uma classe muda com frequência.Se tal situação surgir, será complicado para o programador reescrever o aplicativo e alterar o nome da classe repetidamente.

Em vez disso, ao usar a reflexão, é necessário se preocupar com a possibilidade de uma mudança no nome da classe.

Reflexão é uma API usada para examinar ou modificar o comportamento de métodos, classes, interfaces em tempo de execução.

  1. As aulas obrigatórias para reflexão são fornecidas em java.lang.reflect package.
  2. A reflexão nos dá informações sobre a classe à qual um objeto pertence e também os métodos dessa classe que podem ser executados usando o objeto.
  3. Através da reflexão podemos invocar métodos em tempo de execução, independentemente do especificador de acesso usado com eles.

O java.lang e java.lang.reflect pacotes fornecem classes para reflexão java.

Reflexão pode ser usado para obter informações sobre -

  1. Aula O getClass() O método é usado para obter o nome da classe à qual um objeto pertence.

  2. Construtores O getConstructors() O método é usado para obter os construtores públicos da classe à qual um objeto pertence.

  3. Métodos O getMethods() O método é usado para obter os métodos públicos da classe à qual um objeto pertence.

O API de reflexão é usado principalmente em:

IDE (Ambiente de Desenvolvimento Integrado), por ex.Eclipse, MyEclipse, NetBeans etc.
Depurador e ferramentas de teste, etc.

Vantagens de usar reflexão:

Recursos de extensibilidade: Um aplicativo pode fazer uso de classes externas definidas pelo usuário criando instâncias de objetos de extensibilidade usando seus nomes totalmente qualificados.

Ferramentas de depuração e teste: Os depuradores usam a propriedade de reflexão para examinar membros privados nas classes.

Desvantagens:

Sobrecarga de desempenho: As operações reflexivas têm desempenho mais lento do que suas contrapartes não reflexivas e devem ser evitadas em seções de código que são chamadas com frequência em aplicativos sensíveis ao desempenho.

Exposição de Internos: O código reflexivo quebra abstrações e, portanto, pode mudar o comportamento com atualizações da plataforma.

Referência: Reflexão Java javarevisited.blogspot.in

Reflection é um conjunto de funções que permite acessar as informações de tempo de execução do seu programa e modificar seu comportamento (com algumas limitações).

É útil porque permite alterar o comportamento do tempo de execução dependendo da meta informação do seu programa, ou seja, você pode verificar o tipo de retorno de uma função e alterar a forma como você lida com a situação.

Em C# por exemplo você pode carregar um assembly (um .dll) em tempo de execução e examiná-lo, navegando pelas classes e tomando ações de acordo com o que encontrou.Também permite criar uma instância de uma classe em tempo de execução, invocar seu método, etc.

Onde pode ser útil?Não é útil sempre, mas para situações concretas.Por exemplo, você pode usá-lo para obter o nome da classe para fins de login, para criar dinamicamente manipuladores para eventos de acordo com o que está especificado em um arquivo de configuração e assim por diante...

exemplo simples para reflexão.Em um jogo de xadrez, você não sabe o que será movido pelo usuário em tempo de execução.reflexão pode ser usada para chamar métodos que já estão implementados em tempo de execução.

public class Test {

    public void firstMoveChoice(){
        System.out.println("First Move");
    } 
    public void secondMOveChoice(){
        System.out.println("Second Move");
    }
    public void thirdMoveChoice(){
        System.out.println("Third Move");
    }

    public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { 
        Test test = new Test();
        Method[] method = test.getClass().getMethods();
        //firstMoveChoice
        method[0].invoke(test, null);
        //secondMoveChoice
        method[1].invoke(test, null);
        //thirdMoveChoice
        method[2].invoke(test, null);
    }

}

Reflexão é deixar o objeto ver sua aparência.Este argumento parece não ter nada a ver com reflexão.Na verdade, esta é a capacidade de “autoidentificação”.

A própria reflexão é uma palavra para linguagens que carecem da capacidade de autoconhecimento e autopercepção, como Java e C#.Por não terem a capacidade de autoconhecimento, quando queremos observar como é, devemos ter outra coisa para refletir sobre como é.Excelentes linguagens dinâmicas, como Ruby e Python, podem perceber seu próprio reflexo sem a ajuda de outras pessoas.Podemos dizer que um objeto Java não pode perceber sua aparência sem um espelho, que é um objeto da classe de reflexão, mas um objeto em Python pode percebê-lo sem um espelho.É por isso que precisamos de reflexão em Java.

Eu só quero acrescentar algum ponto a tudo o que foi listado.

Com API de reflexão você pode escrever universal toString() método para qualquer objeto.

É útil na depuração.

Aqui está um exemplo:

class ObjectAnalyzer {

   private ArrayList<Object> visited = new ArrayList<Object>();

   /**
    * Converts an object to a string representation that lists all fields.
    * @param obj an object
    * @return a string with the object's class name and all field names and
    * values
    */
   public String toString(Object obj) {
      if (obj == null) return "null";
      if (visited.contains(obj)) return "...";
      visited.add(obj);
      Class cl = obj.getClass();
      if (cl == String.class) return (String) obj;
      if (cl.isArray()) {
         String r = cl.getComponentType() + "[]{";
         for (int i = 0; i < Array.getLength(obj); i++) {
            if (i > 0) r += ",";
            Object val = Array.get(obj, i);
            if (cl.getComponentType().isPrimitive()) r += val;
            else r += toString(val);
         }
         return r + "}";
      }

      String r = cl.getName();
      // inspect the fields of this class and all superclasses
      do {
         r += "[";
         Field[] fields = cl.getDeclaredFields();
         AccessibleObject.setAccessible(fields, true);
         // get the names and values of all fields
         for (Field f : fields) {
            if (!Modifier.isStatic(f.getModifiers())) {
               if (!r.endsWith("[")) r += ",";
               r += f.getName() + "=";
               try {
                  Class t = f.getType();
                  Object val = f.get(obj);
                  if (t.isPrimitive()) r += val;
                  else r += toString(val);
               } catch (Exception e) {
                  e.printStackTrace();
               }
            }
         }
         r += "]";
         cl = cl.getSuperclass();
      } while (cl != null);

      return r;
   }    
}

Da documentação java página

java.lang.reflect O pacote fornece classes e interfaces para obter informações reflexivas sobre classes e objetos.A reflexão permite acesso programático a informações sobre os campos, métodos e construtores de classes carregadas, e o uso de campos, métodos e construtores refletidos para operar em suas contrapartes subjacentes, dentro das restrições de segurança.

AccessibleObject permite a supressão de verificações de acesso se necessário ReflectPermission está disponível.

As aulas deste pacote, juntamente com java.lang.Class acomodar aplicações como depuradores, intérpretes, inspetores de objetos, navegadores de classes e serviços como Object Serialization e JavaBeans que precisam de acesso aos membros públicos de um objeto de destino (com base em sua classe de tempo de execução) ou aos membros declarados por uma determinada classe

Inclui as seguintes funcionalidades.

  1. Obtendo objetos de classe,
  2. Examinando propriedades de uma classe (campos, métodos, construtores),
  3. Configurando e obtendo valores de campo,
  4. Invocando métodos,
  5. Criando novas instâncias de objetos.

Veja isso documentação link para os métodos expostos por Class aula.

A partir disso artigo (por Dennis Sosnoski, presidente, Sosnoski Software Solutions, Inc) e este artigo (pdf de explorações de segurança):

Posso ver desvantagens consideráveis ​​do uso do Reflection

Usuário de Reflexão:

  1. Ele fornece uma maneira muito versátil de vincular dinamicamente os componentes do programa
  2. É útil para criar bibliotecas que trabalham com objetos de maneiras muito gerais

Desvantagens da reflexão:

  1. A reflexão é muito mais lenta que o código direto quando usado para acesso a campos e métodos.
  2. Pode obscurecer o que realmente está acontecendo dentro do seu código
  3. Ignorar o código-fonte pode criar problemas de manutenção
  4. O código de reflexão também é mais complexo que o código direto correspondente
  5. Ele permite a violação de restrições de segurança Java chave, tais como proteção de acesso a dados e segurança de tipo

Abusos gerais:

  1. Carregamento de aulas restritas,
  2. Obtenção de referências a construtores, métodos ou campos de uma classe restrita,
  3. Criação de novas instâncias de objetos, invocação de métodos, obtenção ou configuração de valores de campos de uma classe restrita.

Dê uma olhada nesta pergunta do SE sobre abuso do recurso de reflexão:

Como leio um campo privado em Java?

Resumo:

O uso inseguro de suas funções conduzidas a partir de um código de sistema também pode facilmente levar ao comprometimento de um modo de segurança Javaeu. Portanto, use esse recurso com moderação

Como o próprio nome sugere, ele reflete o que contém, por exemplo, método de classe, etc., além de fornecer recurso para invocar método criando instância dinamicamente em tempo de execução.

Ele é usado por muitos frameworks e aplicativos para invocar serviços sem realmente conhecer o código.

Reflection tem muitos usa.O que estou mais familiarizado é a capacidade de criar código dinamicamente.

Ou seja:classes dinâmicas, funções, construtores - com base em quaisquer dados (xml/array/sql results/hardcoded/etc.)

O Reflection oferece a capacidade de escrever código mais genérico.Ele permite que você crie um objeto em tempo de execução e chame seu método em tempo de execução.Conseqüentemente, o programa pode ser altamente parametrizado.Também permite a introspecção do objeto e da classe para detectar suas variáveis ​​​​e métodos expostos ao mundo exterior.

Quero responder a esta pergunta com um exemplo.Em primeiro lugar Hibernate usos do projeto Reflection API para gerar CRUD instruções para preencher a lacuna entre o aplicativo em execução e o armazenamento de persistência.Quando as coisas mudam no domínio, o Hibernate precisa saber sobre eles para persisti-los no armazenamento de dados e vice-versa.

Alternativamente funciona Lombok Project.Ele apenas injeta código em tempo de compilação, resultando na inserção de código em suas classes de domínio.(Acho que está tudo bem para getters e setters)

Hibernate escolheu reflection porque tem impacto mínimo no processo de construção de um aplicativo.

E do Java 7 temos MethodHandles, que funciona como Reflection API.Em projetos, para trabalhar com loggers basta copiar e colar o próximo código:

Logger LOGGER = Logger.getLogger(MethodHandles.lookup().lookupClass().getName());

Porque é difícil cometer erros de digitação neste caso.

Como acho melhor explicar por exemplo e nenhuma das respostas parece fazer isso...

Um exemplo prático de uso de reflexões seria um Java Language Server escrito em Java ou um PHP Language Server escrito em PHP, etc.O Language Server oferece ao seu IDE recursos como preenchimento automático, salto para definição, ajuda de contexto, tipos de dicas e muito mais.Para que todos os nomes de tags (palavras que podem ser preenchidas automaticamente) correspondam quando você pressiona, digamos, ABA e mostrar todas as dicas, o Language Server deve inspecionar tudo sobre a classe, incluindo seus blocos de documentos e membros privados.Para isso é necessária uma reflexão dessa classe.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top