¿Qué es la reflexión y por qué es útil?

2125

¿Qué es la reflexión y por qué es útil?

Estoy particularmente interesado en Java, pero supongo que los principios son los mismos en cualquier lenguaje.

Lehane
fuente
99
Para mí es una forma de obtener nombres de clase en tiempo de ejecución y crear objetos de esa clase.
Nabin
65
porque esta es una pregunta popular, me gustaría señalar que la reflexión (sin anotaciones) debería ser la última herramienta a la que recurras para resolver un problema. Lo uso y me encanta, pero cancela todas las ventajas del tipeo estático de Java. Si lo necesita, aíslelo en un área lo más pequeña posible (un método o una clase). Es más aceptable usarlo en pruebas que el código de producción. Con anotaciones, debería estar bien. El punto principal no es especificar nombres de clase o método como "Cadenas" si es posible evitarlo.
Bill K
44
Además del comentario de @ BillK: la reflexión es muy poderosa, yo lo llamaría mágico. Con un gran poder viene una gran responsabilidad. Úselo solo si sabe lo que está haciendo.
MC Emperor
Puede evitar muchas de las trampas involucradas con la reflexión usando Manifold @Jailbreak. Proporciona acceso directo y de tipo seguro a campos privados, métodos, etc. Deje que el compilador de Java verifique de manera segura su código y permita que Manifold genere el acceso de reflexión subyacente para usted. Más información: manifold.systems/docs.html#type-safe-reflection
Scott

Respuestas:

1717

El reflejo del nombre se usa para describir el código que puede inspeccionar otro código en el mismo sistema (o en sí mismo).

Por ejemplo, supongamos que tiene un objeto de un tipo desconocido en Java, y le gustaría llamar a un método 'doSomething' si existe. El sistema de tipeo estático de Java no está realmente diseñado para soportar esto a menos que el objeto se ajuste a una interfaz conocida, pero usando la reflexión, su código puede mirar el objeto y descubrir si tiene un método llamado 'doSomething' y luego llamarlo si usted querer.

Entonces, para darle un ejemplo de código de esto en Java (imagine que el objeto en cuestión es foo):

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

Un caso de uso muy común en Java es el uso con anotaciones. JUnit 4, por ejemplo, utilizará la reflexión para buscar en sus clases los métodos etiquetados con la anotación @Test, y luego los llamará cuando ejecute la prueba unitaria.

Hay algunos buenos ejemplos de reflexión para comenzar en http://docs.oracle.com/javase/tutorial/reflect/index.html

Y finalmente, sí, los conceptos son bastante similares en otros lenguajes estáticamente tipados que admiten la reflexión (como C #). En lenguajes de tipo dinámico, el caso de uso descrito anteriormente es menos necesario (ya que el compilador permitirá que se llame a cualquier método en cualquier objeto, fallando en tiempo de ejecución si no existe), pero el segundo caso es buscar métodos que están marcados o el trabajo de cierta manera sigue siendo común.

Actualización de un comentario:

La capacidad de inspeccionar el código en el sistema y ver los tipos de objetos no es una reflexión, sino una introspección de tipos. La reflexión es, entonces, la capacidad de realizar modificaciones en tiempo de ejecución haciendo uso de la introspección. La distinción es necesaria aquí ya que algunos lenguajes admiten la introspección, pero no admiten la reflexión. Un ejemplo de esto es C ++

Matt Sheppard
fuente
32
¿Puede explicar cuál es el significado de ese parámetro nulo en esta línea? Método método = foo.getClass (). getMethod ("doSomething", null);
Krsna Chaitanya
54
El valor nulo indica que no se están pasando parámetros al método foo. Ver docs.oracle.com/javase/6/docs/api/java/lang/reflect/… , java.lang.Object ...) para más detalles.
Matt Sheppard
791
Solo para aclarar, ya que esto tiene muchos votos a favor. La capacidad de inspeccionar el código en el sistema y ver los tipos de objetos no es una reflexión, sino una introspección de tipos. La reflexión es, entonces, la capacidad de realizar modificaciones en tiempo de ejecución haciendo uso de la introspección. La distinción es necesaria aquí ya que algunos lenguajes admiten la introspección, pero no admiten la reflexión. Un ejemplo de esto es C ++.
bigtunacan
39
Me encanta la reflexión, pero si tienes control sobre el código, entonces usar la reflexión como se especifica en esta respuesta no es práctica y, por lo tanto, es un abuso: debes usar la introspección de tipo (instancia de) y los tipos fuertes. Si hay algo más que reflexión para hacer algo, así es como debe hacerse. La reflexión causa una gran angustia porque pierde todas las ventajas de usar un lenguaje tipado estáticamente. Si lo necesita, lo necesita, sin embargo, incluso entonces consideraría una solución preempaquetada como Spring o algo que encapsule por completo la reflexión necesaria: IE: deje que alguien más tenga dolores de cabeza.
Bill K
66
@bigtunacan ¿De dónde obtuvo esa información? Veo el término "reflexión" utilizado en la documentación oficial de Java de Oracle para describir no solo la capacidad de realizar cambios en tiempo de ejecución sino también la capacidad de ver el tipo de un objeto. Por no mencionar que la mayoría de la llamada "tipo de introspección" clases relacionadas (por ejemplo: Method, Constructor, Modifier, Field, Member,, básicamente, al parecer, todos excepto Class) están dentro del java.lang.*reflect*paquete. ¿Quizás el concepto "reflexión" incluye tanto la "introspección de tipo" como la modificación en tiempo de ejecución?
RestInPeace
246

La reflexión es la capacidad de un lenguaje para inspeccionar y llamar dinámicamente clases, métodos, atributos, etc. en tiempo de ejecución.

Por ejemplo, todos los objetos en Java tienen el método getClass(), que le permite determinar la clase del objeto incluso si no lo conoce en tiempo de compilación (por ejemplo, si lo declaró como un Object), esto puede parecer trivial, pero tal reflexión no es posible en lenguajes menos dinámicos como C++. Los usos más avanzados le permiten enumerar y llamar a métodos, constructores, etc.

La reflexión es importante, ya que le permite escribir programas que no tienen que "saber" todo en tiempo de compilación, lo que los hace más dinámicos, ya que pueden vincularse en tiempo de ejecución. El código se puede escribir en interfaces conocidas, pero las clases reales que se utilizarán se pueden instanciar utilizando la reflexión de los archivos de configuración.

Muchos marcos modernos usan la reflexión ampliamente por esta misma razón. La mayoría de los otros lenguajes modernos también usan la reflexión, y en los lenguajes de script (como Python) están aún más integrados, ya que se siente más natural dentro del modelo de programación general de esos lenguajes.

Liedman
fuente
2
En otras palabras, puede crear una instancia a partir de su nombre calificado y el compilador no se quejará (porque digamos que usa solo una Cadena para el nombre de la clase). Luego, en tiempo de ejecución, si esa clase no está presente, obtienes una excepción. En este caso, evitaste el compilador. ¿Me darías algún caso de uso específico para esto? Simplemente no puedo imaginar cuándo lo elegiría.
Fernando Gabrieli
3
@FernandoGabrieli, si bien es cierto que es fácil crear errores de tiempo de ejecución con reflexión, también es perfectamente posible utilizar la reflexión sin arriesgarse a excepciones de tiempo de ejecución. Como se insinuó en mi respuesta, un uso común de la reflexión es para bibliotecas o marcos, que explícitamente no pueden conocer la estructura de la aplicación en el momento de la compilación, ya que se compilan por separado de la aplicación. Es probable que cualquier biblioteca que use "código por convención" use reflexión, pero no necesariamente usando cadenas mágicas.
Liedman
1
C++tiene información de tipo de tiempo de ejecución. RTTI
_Static_assert
114

Uno de mis usos favoritos de la reflexión es el siguiente método de volcado de Java. Toma cualquier objeto como parámetro y utiliza la API de reflexión de Java para imprimir cada nombre y 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();
}
Ben Williams
fuente
8
¿A qué se debe configurar Callcount?
Tom
8
Obtuve una excepción en el hilo "AWT-EventQueue-0" java.lang.StackOverflowError cuando ejecuté esto.
Tom
3
@Tom callCountdebe establecerse en cero. Su valor se utiliza para determinar cuántas pestañas deben preceder a cada línea de salida: cada vez que dump necesita volcar un "subobject", la salida se imprimirá como anidada en el padre. Ese método resulta útil cuando se envuelve en otro. Considere printDump(Object obj){ System.out.println(dump(obj, 0)); }.
fny
1
El java.lang.StackOverflowError podría crearse en caso de referencias circulares, debido a la recursión no verificada: buffer.append (dump (value, callCount))
Arnaud P
44
¿Puedes liberar específicamente tu código al dominio público, bastante por favor?
stolsvik
85

Usos de la reflexión

La reflexión es utilizada comúnmente por programas que requieren la capacidad de examinar o modificar el comportamiento en tiempo de ejecución de las aplicaciones que se ejecutan en la máquina virtual Java. Esta es una característica relativamente avanzada y debe ser utilizada solo por desarrolladores que tengan una buena comprensión de los fundamentos del lenguaje. Con esa advertencia en mente, la reflexión es una técnica poderosa y puede permitir que las aplicaciones realicen operaciones que de otro modo serían imposibles.

Características de extensibilidad

Una aplicación puede hacer uso de clases externas definidas por el usuario creando instancias de objetos de extensibilidad utilizando sus nombres totalmente calificados. Navegadores de clases y entornos de desarrollo visual Un navegador de clases debe poder enumerar los miembros de las clases. Los entornos de desarrollo visual pueden beneficiarse al hacer uso de la información de tipo disponible en la reflexión para ayudar al desarrollador a escribir el código correcto. Depuradores y herramientas de prueba Los depuradores deben poder examinar a los miembros privados en las clases. Los arneses de prueba pueden hacer uso de la reflexión para llamar sistemáticamente a un conjunto de APIs descubribles definidos en una clase, para garantizar un alto nivel de cobertura de código en un conjunto de pruebas.

Inconvenientes de la reflexión

La reflexión es poderosa, pero no debe usarse indiscriminadamente. Si es posible realizar una operación sin usar la reflexión, entonces es preferible evitar usarla. Deben tenerse en cuenta las siguientes inquietudes al acceder al código a través de la reflexión.

  • Rendimiento de arriba

Debido a que la reflexión involucra tipos que se resuelven dinámicamente, ciertas optimizaciones de máquinas virtuales Java no se pueden realizar. En consecuencia, las operaciones reflectantes tienen un rendimiento más lento que sus contrapartes no reflectantes y deben evitarse en secciones de código que se llaman con frecuencia en aplicaciones sensibles al rendimiento.

  • Restricciones de seguridad

Reflection requiere un permiso de tiempo de ejecución que puede no estar presente cuando se ejecuta bajo un administrador de seguridad. Esto es una consideración importante para el código que debe ejecutarse en un contexto de seguridad restringido, como en un Applet.

  • Exposición de internos

Dado que la reflexión permite que el código realice operaciones que serían ilegales en el código no reflectante, como acceder a campos y métodos privados, el uso de la reflexión puede provocar efectos secundarios inesperados, que pueden hacer que el código sea disfuncional y destruir la portabilidad. El código reflexivo rompe las abstracciones y, por lo tanto, puede cambiar el comportamiento con las actualizaciones de la plataforma.

fuente: La API de Reflection

Desmond Smith
fuente
44

¡Reflection es un mecanismo clave para permitir que una aplicación o marco funcione con código que aún no se ha escrito!

Tome por ejemplo su típico archivo web.xml. Esto contendrá una lista de elementos de servlet, que contienen elementos de clase servlet anidados. El contenedor de servlets procesará el archivo web.xml y creará una nueva instancia de cada clase de servlet a través de la reflexión.

Otro ejemplo sería la API de Java para análisis XML (JAXP) . Cuando un proveedor de analizador XML se 'conecta' a través de propiedades de sistema bien conocidas, que se utilizan para construir nuevas instancias a través de la reflexión.

Y, por último, el ejemplo más completo es Spring, que utiliza la reflexión para crear sus granos, y por su uso intensivo de proxies.

kit de herramientas
fuente
36

No todos los idiomas admiten la reflexión, pero los principios suelen ser los mismos en los idiomas que lo admiten.

La reflexión es la capacidad de "reflexionar" sobre la estructura de su programa. O más concreto. Para ver los objetos y las clases que tiene y obtener información mediante programación sobre los métodos, campos e interfaces que implementan. También puedes mirar cosas como anotaciones.

Es útil en muchas situaciones. En cualquier lugar que desee poder conectar dinámicamente clases en su código. Muchos de los mapeadores relacionales de objetos usan la reflexión para poder instanciar objetos de bases de datos sin saber de antemano qué objetos van a usar. Las arquitecturas enchufables son otro lugar donde la reflexión es útil. Poder cargar código dinámicamente y determinar si hay tipos allí que implementen la interfaz correcta para usar como complemento es importante en esas situaciones.

Mendelt
fuente
Necesito instanciar objetos basados ​​en los datos presentes en DB. Creo que esto es lo que estás diciendo. El código de muestra me ayudaría mucho. Gracias por adelantado.
Atom
34

La reflexión permite la creación de instancias de nuevos objetos, la invocación de métodos y las operaciones get / set en variables de clase dinámicamente en tiempo de ejecución sin tener conocimiento previo de su implementación.

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");

En el ejemplo anterior, el parámetro nulo es el objeto sobre el que desea invocar el método. Si el método es estático, debe proporcionar nulo. Si el método no es estático, al invocarlo debe proporcionar una instancia válida de MyObject en lugar de nula.

Reflection también le permite acceder a miembros / métodos privados de una clase:

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 la inspección de clases (también conocido como introspección) no necesita importar el paquete de reflexión ( java.lang.reflect). Se puede acceder a los metadatos de la clase a través de java.lang.Class.

Reflection es una API muy poderosa, pero puede ralentizar la aplicación si se usa en exceso, ya que resuelve todos los tipos en tiempo de ejecución.

Nikhil Shekhar
fuente
21

Java Reflection es bastante potente y puede ser muy útil. Java Reflection hace posible inspeccionar clases, interfaces, campos y métodos en tiempo de ejecución, sin conocer los nombres de las clases, métodos, etc. en tiempo de compilación. También es posible crear instancias de nuevos objetos, invocar métodos y obtener / establecer valores de campo utilizando la reflexión.

Un ejemplo rápido de Java Reflection para mostrarle cómo se ve el uso de la reflexión:

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

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

Este ejemplo obtiene el objeto Class de la clase llamada MyObject. Usando el objeto de clase, el ejemplo obtiene una lista de los métodos en esa clase, itera los métodos e imprime sus nombres.

Aquí se explica exactamente cómo funciona todo esto

Editar : Después de casi 1 año, estoy editando esta respuesta, ya que mientras leía sobre la reflexión, obtuve algunos usos más de Reflection.

  • Spring utiliza la configuración de frijol como:


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

Cuando el contexto Spring procesa este elemento <bean>, usará Class.forName (String) con el argumento "com.example.Foo" para crear una instancia de esa clase.

Luego volverá a usar la reflexión para obtener el setter apropiado para el elemento <property> y establecer su valor en el valor especificado.

  • Junit usa Reflection especialmente para probar 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);
VdeX
fuente
21

Ejemplo:

Tomemos, por ejemplo, una aplicación remota que le da a su aplicación un objeto que obtiene utilizando sus métodos API. Ahora, según el objeto, es posible que deba realizar algún tipo de cálculo.

El proveedor garantiza que el objeto puede ser de 3 tipos y necesitamos realizar cálculos basados ​​en qué tipo de objeto.

Por lo tanto, podríamos implementar en 3 clases, cada una de las cuales contiene una lógica diferente. Obviamente, la información del objeto está disponible en tiempo de ejecución, por lo que no puede codificar estáticamente para realizar el cálculo, por lo tanto, la reflexión se utiliza para instanciar el objeto de la clase que necesita para realizar el cálculo en función del objeto recibido del proveedor.

human.js
fuente
Necesito algo similar ... Un ejemplo me ayudaría mucho, ya que soy nuevo en los conceptos de reflexión ...
Atom
2
Estoy confundido: ¿no puedes usar instanceofpara determinar el tipo de objeto en tiempo de ejecución?
ndm13
19

Ejemplo simple para la reflexión. En un juego de ajedrez, no sabes qué moverá el usuario en tiempo de ejecución. La reflexión se puede utilizar para llamar a métodos que ya están implementados en tiempo de ejecución:

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);
    }

}
Isuru Jayakantha
fuente
18

Reflection es una API que se utiliza para examinar o modificar el comportamiento de métodos, clases e interfaces en tiempo de ejecución.

  1. Las clases requeridas para la reflexión se proporcionan en java.lang.reflect package.
  2. La reflexión nos da información sobre la clase a la que pertenece un objeto y también los métodos de esa clase que pueden ejecutarse utilizando el objeto.
  3. A través de la reflexión podemos invocar métodos en tiempo de ejecución independientemente del especificador de acceso utilizado con ellos.

Los paquetes java.langy java.lang.reflectproporcionan clases para la reflexión de Java.

La reflexión se puede utilizar para obtener información sobre:

  1. Clase El getClass()método se utiliza para obtener el nombre de la clase a la que pertenece un objeto.

  2. Constructores El getConstructors()método se utiliza para obtener los constructores públicos de la clase a la que pertenece un objeto.

  3. Métodos El getMethods()método se utiliza para obtener los métodos públicos de la clase a la que pertenece un objeto.

La API de Reflection se usa principalmente en:

IDE (entorno de desarrollo integrado), por ejemplo, Eclipse, MyEclipse, NetBeans, etc.
Depurador y herramientas de prueba, etc.

Ventajas de usar la reflexión:

Características de extensibilidad: una aplicación puede hacer uso de clases externas definidas por el usuario mediante la creación de instancias de objetos de extensibilidad utilizando sus nombres totalmente calificados.

Herramientas de depuración y prueba: los depuradores usan la propiedad de reflexión para examinar a los miembros privados en las clases.

Inconvenientes:

Sobrecarga de rendimiento: las operaciones reflectantes tienen un rendimiento más lento que sus contrapartes no reflectantes, y deben evitarse en secciones de código que se llaman con frecuencia en aplicaciones sensibles al rendimiento.

Exposición de elementos internos: el código reflexivo rompe las abstracciones y, por lo tanto, puede cambiar el comportamiento con las actualizaciones de la plataforma.

Ref: Java Reflection javarevisited.blogspot.in

roottraveller
fuente
44
Añadiría a los inconvenientes " Se rompe la refactorización ". Para mí esa es la razón principal para evitar la reflexión tanto como sea posible.
SantiBailors
Entonces, nos permite (por ejemplo) inspeccionar las clases que tenemos (si tenemos instancias de ellas o no), ¿correcto? Con esto quiero decir, obtener sus métodos o constructores y usarlos para crear nuevas instancias / invocarlas. ¿Por qué decimos "cambiar el comportamiento del programa" si el comportamiento ya está allí pero con un código diferente? ¿Por qué se llama "reflexión"? Gracias
Fernando Gabrieli
15

Según mi entendimiento:

La reflexión permite al programador acceder a las entidades en el programa de forma dinámica. es decir, mientras codifica una aplicación si el programador desconoce una clase o sus métodos, puede utilizar dicha clase dinámicamente (en tiempo de ejecución) mediante el uso de la reflexión.

Se usa con frecuencia en escenarios donde el nombre de una clase cambia con frecuencia. Si surge tal situación, es complicado para el programador reescribir la aplicación y cambiar el nombre de la clase una y otra vez.

En cambio, al usar la reflexión, es necesario preocuparse por un posible nombre de clase cambiante.

pramod
fuente
15

Reflection es un conjunto de funciones que le permite acceder a la información de tiempo de ejecución de su programa y modificar su comportamiento (con algunas limitaciones).

Es útil porque le permite cambiar el comportamiento del tiempo de ejecución dependiendo de la metainformación de su programa, es decir, puede verificar el tipo de retorno de una función y cambiar la forma en que maneja la situación.

En C #, por ejemplo, puede cargar un ensamblado (un .dll) en tiempo de ejecución y examinarlo, navegar a través de las clases y realizar acciones de acuerdo con lo que encontró. También le permite crear una instancia de una clase en tiempo de ejecución, invocar su método, etc.

¿Dónde puede ser útil? No es útil siempre sino para situaciones concretas. Por ejemplo, puede usarlo para obtener el nombre de la clase para fines de registro, para crear dinámicamente controladores para eventos de acuerdo con lo que se especifica en un archivo de configuración, etc.

Jorge Córdoba
fuente
11

Solo quiero agregar algún punto a todo lo que estaba en la lista.

Con Reflection API puedes escribir un toString()método universal para cualquier objeto.

Es útil en la depuración.

Aquí hay un ejemplo:

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;
   }    
}
nazar_art
fuente
11

La reflexión es dejar que el objeto vea su apariencia. Este argumento no parece tener nada que ver con la reflexión. De hecho, esta es la habilidad de "autoidentificación".

Reflexión en sí es una palabra para tales lenguajes que carecen de la capacidad de autoconocimiento y autodetección como Java y C #. Debido a que no tienen la capacidad de autoconocimiento, cuando queremos observar cómo se ve, debemos tener otra cosa para reflexionar sobre cómo se ve. Excelentes lenguajes dinámicos como Ruby y Python pueden percibir su propio reflejo sin la ayuda de otras personas. Podemos decir que el objeto de Java no puede percibir cómo se ve sin un espejo, que es un objeto de la clase de reflexión, pero un objeto en Python puede percibirlo sin un espejo. Por eso necesitamos reflexión en Java.

Marcus Thornton
fuente
type (), isinstance (), invocable (), dir () y getattr (). .... estas son llamadas de reflexión pitónica
AnthonyJClink
9

Desde la página de documentación de Java

java.lang.reflectEl paquete proporciona clases e interfaces para obtener información reflexiva sobre clases y objetos. Reflection permite el acceso programático a información sobre los campos, métodos y constructores de clases cargadas, y el uso de campos, métodos y constructores reflejados para operar en sus contrapartes subyacentes, dentro de las restricciones de seguridad.

AccessibleObjectpermite la supresión de las comprobaciones de acceso si ReflectPermissiones necesario .

Las clases en este paquete, junto con java.lang.Classaplicaciones de acomodo como depuradores, intérpretes, inspectores de objetos, buscadores de clases y servicios como Object Serializationy JavaBeansque necesitan acceso a los miembros públicos de un objeto de destino (en función de su clase de tiempo de ejecución) o los miembros declarados por una clase dada

Incluye la siguiente funcionalidad.

  1. Obteniendo objetos de clase,
  2. Examinando las propiedades de una clase (campos, métodos, constructores),
  3. Establecer y obtener valores de campo,
  4. Invocando métodos,
  5. Crear nuevas instancias de objetos.

Eche un vistazo a este enlace de documentación para conocer los métodos expuestos por Classclase.

De este artículo (por Dennis Sosnoski, Presidente, Sosnoski Software Solutions, Inc) y este artículo (security-explorations pdf):

Puedo ver inconvenientes considerables que los usos de usar Reflection

Usuario de Reflexión:

  1. Proporciona una forma muy versátil de vincular dinámicamente los componentes del programa.
  2. Es útil para crear bibliotecas que funcionan con objetos de manera muy general.

Inconvenientes de la reflexión:

  1. La reflexión es mucho más lenta que el código directo cuando se usa para acceso de campo y método.
  2. Puede ocultar lo que realmente sucede dentro de su código
  3. Omite el código fuente puede crear problemas de mantenimiento
  4. El código de reflexión también es más complejo que el código directo correspondiente
  5. Permite la violación de restricciones de seguridad clave de Java, como la protección de acceso a datos y la seguridad de tipos

Abusos generales:

  1. Carga de clases restringidas,
  2. Obteniendo referencias a constructores, métodos o campos de una clase restringida,
  3. Creación de nuevas instancias de objetos, invocación de métodos, obtención o configuración de valores de campo de una clase restringida.

Eche un vistazo a esta pregunta SE con respecto a la función de abuso de reflexión:

¿Cómo leo un campo privado en Java?

Resumen:

El uso inseguro de sus funciones realizadas desde un código del sistema también puede llevar fácilmente al compromiso de un modo de seguridad Java l. Así que usa esta función con moderación

Ravindra babu
fuente
Una forma de evitar los problemas de rendimiento de Reflection en algunos casos es hacer que una clase Woozleexamine otras clases en el inicio para ver cuáles tienen un RegisterAsWoozleHelper()método estático e invoque todos los métodos que encuentre con una devolución de llamada que puedan usar para hablar Woozlesobre ellos mismos, evitando necesita usar Reflection mientras, por ejemplo, deserializa datos.
supercat
9

Como su propio nombre sugiere, refleja lo que contiene, por ejemplo, el método de clase, etc., además de proporcionar características para invocar el método creando instancia dinámicamente en tiempo de ejecución.

Es utilizado por muchos frameworks y aplicaciones bajo el bosque para invocar servicios sin conocer realmente el código.

Mohammed Sarfaraz
fuente
7

Reflection te da la capacidad de escribir más código genérico. Le permite crear un objeto en tiempo de ejecución y llamar a su método en tiempo de ejecución. Por lo tanto, el programa puede ser altamente parametrizado. También permite introspectar el objeto y la clase para detectar sus variables y métodos expuestos al mundo exterior.

saumik gupta
fuente
6

ReflectionTiene muchos usos . El que estoy más familiarizado es poder crear código sobre la marcha.

IE: clases dinámicas, funciones, constructores - basados ​​en cualquier dato (xml / array / sql results / hardcoded / etc.)

Ess Kay
fuente
1
Esta respuesta sería mucho mejor si proporcionara solo un ejemplo inusual de código generado, ya sea a partir de un resultado SQL o un archivo XML, etc.
ThisClark
No hay problema. Usé la reflexión en una aplicación de Windows que genera dinámicamente la interfaz basada en XML tomado de una base de datos.
Ess Kay
Básicamente, hay una clase que creé que muestra al usuario un informe. Este informe tiene parámetros como Date (from to) id, o cualquier otra cosa. Esta información se almacena en xml. Entonces, primero tenemos una selección de informes. Según el informe seleccionado, el formulario obtiene el xml. Una vez que se recupera el xml, utiliza la reflexión para crear una clase con campos basados ​​en tipos reflejados. Una vez que cambie a un informe diferente, la pizarra se borrará y se generarán nuevos campos basados ​​en el xml. Es esencialmente una forma dinámica basada en la reflexión. También lo usé de otras maneras, pero esto debería ser suficiente Esperanza que ayude
Ess Kay
3

Quiero responder a esta pregunta con un ejemplo. En primer lugar, el Hibernateproyecto se utiliza Reflection APIpara generar CRUDdeclaraciones para cerrar el abismo entre la aplicación en ejecución y el almacén de persistencia. Cuando las cosas cambian en el dominio, Hibernatedeben saber sobre ellas para persistirlas en el almacén de datos y viceversa.

Alternativamente funciona Lombok Project. Simplemente inyecta código en tiempo de compilación, lo que hace que el código se inserte en sus clases de dominio. (Creo que está bien para getters y setters)

Hibernateelegido reflectionporque tiene un impacto mínimo en el proceso de compilación de una aplicación.

Y de Java 7 tenemos MethodHandles, que funciona como Reflection API. En proyectos, para trabajar con registradores, simplemente copiamos y pegamos el siguiente código:

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

Porque es difícil cometer un error tipográfico en este caso.

BSeitkazin
fuente
3

Como creo que es mejor explicar con el ejemplo y ninguna de las respuestas parece hacer eso ...

Un ejemplo práctico de uso de reflexiones sería un servidor de lenguaje Java escrito en Java o un servidor de lenguaje PHP escrito en PHP, etc. El servidor de lenguaje brinda sus capacidades IDE como autocompletar, saltar a definición, ayuda contextual, tipos de sugerencias y más. Para que todos los nombres de etiquetas (palabras que se pueden completar automáticamente) muestren todas las coincidencias posibles a medida que escribe, el servidor de idiomas debe inspeccionar todo lo relacionado con la clase, incluidos los bloques de documentos y los miembros privados. Para eso necesita un reflejo de dicha clase.

Un ejemplo diferente sería una prueba unitaria de un método privado. Una forma de hacerlo es crear una reflexión y cambiar el alcance del método a público en la fase de configuración de la prueba. Por supuesto, uno puede argumentar que los métodos privados no deberían probarse directamente, pero ese no es el punto.

cprn
fuente