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


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

Estoy particularmente interesado en Java, pero asumo que los principios son los mismos en cualquier idioma.


1690
2017-09-01 08:39


origen


Respuestas:


El nombre de reflexión se usa para describir un código que puede inspeccionar otro código en el mismo sistema (o 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 tipado estático de Java no está diseñado para admitir esto a menos que el objeto se ajuste a una interfaz conocida, pero al usar la reflexión, el código puede mirar el objeto y descubrir si tiene un método llamado 'hacer algo' y luego llamarlo si 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, usará la reflexión para mirar a través de 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, por último, sí, los conceptos son bastante similares en otros tipos de lenguajes estáticos que admiten la reflexión (como C #). En los lenguajes tipados dinámicamente, el caso de uso descrito anteriormente es menos necesario (ya que el compilador permitirá llamar a cualquier método sobre cualquier objeto, fallando en tiempo de ejecución si no existe), pero el segundo caso busca métodos que están marcados o trabajar de cierta manera todavía es común.

Actualización de un comentario:

La capacidad de inspeccionar el código en el sistema y ver los tipos de objetos es   no reflejo, sino Tipo Introspección. La reflexión es entonces la   capacidad de hacer modificaciones en tiempo de ejecución haciendo uso de   introspección. La distinción es necesaria aquí ya que algunos idiomas   apoye la introspección, pero no apoye la reflexión. Un tal ejemplo   es C ++


1414
2017-09-01 08:44



Reflexión es la capacidad de un idioma 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(), lo que le permite determinar la clase del objeto incluso si no lo conoce en el momento de la 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 contra 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 extensivamente por esta misma razón. La mayoría de los otros lenguajes modernos también usan la reflexión, y en los lenguajes de scripting (como Python) están incluso más estrechamente integrados, ya que se siente más natural dentro del modelo de programación general de esos idiomas.


195
2017-09-01 08:52



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 reflejo de Java para imprimir el nombre y el valor de cada 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();
}

84
2017-09-02 16:15



Usos de la reflexión

La reflexión es comúnmente utilizada por programas que requieren la capacidad de examinar o modificar el comportamiento en el 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 mediante la creación de instancias de objetos de extensibilidad utilizando sus nombres totalmente calificados. Navegadores de clase y entornos de desarrollo visual Un buscador de clases necesita poder enumerar los miembros de las clases. Los entornos de desarrollo visual pueden beneficiarse del uso de la información de tipo disponible en reflejo 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 usar la reflexión para llamar sistemáticamente a un conjunto de API reconocibles definidas 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, es preferible evitar usarla. Las siguientes inquietudes deben tenerse en cuenta al acceder al código a través de la reflexión.

  • Rendimiento aéreo

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 reflexivas tienen un rendimiento más lento que sus equivalentes no reflectantes y deben evitarse en las secciones de código que se llaman con frecuencia en las aplicaciones sensibles al rendimiento.

  • Restricciones de seguridad

La reflexión requiere un permiso de tiempo de ejecución que puede no estar presente cuando se ejecuta bajo un administrador de seguridad. Esta 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

Como la reflexión permite que el código realice operaciones que serían ilegales en código no reflexivo, como el acceso a campos y métodos privados, el uso de la reflexión puede producir efectos secundarios inesperados, lo que puede hacer que el código sea disfuncional y destruya 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 reflexión


55
2017-10-17 11:59



La reflexión es un mecanismo clave para permitir que una aplicación o un marco funcione con código que podría no haberse escrito aún.

Tomemos como ejemplo su típico archivo web.xml. Esto contendrá una lista de elementos de servlet, que contienen elementos de clase de 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 el análisis XML (JAXP). Donde un proveedor de analizador XML está 'conectado' a través de propiedades de sistema bien conocidas, que se utilizan para construir nuevas instancias a través de la reflexión.

Y finalmente, el ejemplo más completo es Primavera que utiliza la reflexión para crear sus granos, y para su uso intensivo de proxies


30
2017-09-01 09:30



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

La reflexión es la capacidad de "reflejar" en la estructura de su programa. O más concreto. Para ver los objetos y clases que tiene y obtener información de forma programática sobre los métodos, campos e interfaces que implementan. También puede ver cosas como anotaciones.

Es útil en muchas situaciones. Donde quiera, puede conectar dinámicamente las clases a su código. Muchos mapeadores relacionales de objetos usan la reflexión para poder crear instancias de objetos de bases de datos sin saber de antemano qué objetos van a usar. Las arquitecturas de plugins son otro lugar donde la reflexión es útil. Ser capaz de cargar código dinámicamente y determinar si hay tipos allí que implementen la interfaz correcta para usar como un complemento es importante en esas situaciones.


27
2017-09-01 08:50



Reflection permite la creación de instancias de objetos nuevos, la invocación de métodos y 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, proporcione nulo. Si el método no es estático, al invocarlo debe proporcionar una instancia válida de MyObject en lugar de null.

La reflexión 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 conocida como introspección), no es necesario importar el paquete de reflexión (java.lang.reflect) Se puede acceder a los metadatos de clase a través de java.lang.Class.

La reflexión 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.


26
2017-07-08 16:12



Ejemplo:
Tomemos, por ejemplo, una aplicación remota que le da a su aplicación un objeto que obtiene usando sus métodos API. Ahora, en función del 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 un cálculo basado en qué tipo de objeto.
Entonces podemos implementar en 3 clases cada una que contenga una lógica diferente. Obviamente, la información del objeto está disponible en tiempo de ejecución para que no pueda codificar estáticamente para realizar cálculos, por lo tanto, la reflexión se usa para crear el objeto de la clase que se requiere para realizar el cálculo basado en objeto recibido del proveedor.


18
2018-06-22 15:35