Reflection

Alberto Ferrari
Ingegneria dell'Informazione, UniPR

Reflection

  • Le API reflection nel package java.lang.reflection sono un insieme di classi pensate per l’interrogazione dinamica di istanze di classi java (proprietà riflessive).
  • Di una generica classe possiamo conoscere
    • i metodi
    • gli attributi
    • e di tutto ciò che essa contiene, direttamente durante la sua esecuzione.
    • è possibile interrogarne i metodi, o invocarli, come se ci si trovasse in un normale flusso di esecuzione.

Violare l'incapsulamento

  • La violazione dell’incapsulamento può essere un problema al paradigma object oriented ma, se usata con le giuste accortezze, la reflection diventa un potentissimo strumento utile allo sviluppo di framework e metodologie riutilizzabili.
  • Ha, per esempio, interessantissime applicazioni nella trasposizione di dati da un tipo ad un altro:
    • molto spesso si usa la reflection per convertire documenti XML in oggetti Java e viceversa.

Esempio

public class ReflectionUtils {

    public static String getClassName(Object aClass) {
        return aClass.getClass().getSimpleName();
    }

    // Tramite l'oggetto di cui non conosco la classe mi faccio
    // restituire i suoi attributi in un array di stringhe
    public static String[] getStringFields(Object aClass) {
        Field[]  campi = aClass.getClass().getDeclaredFields();
        String[] campiStringa = new String[campi.length];

        for (int i = 0; i < campi.length; i++) {
            campiStringa[i] = campi[i].getName();
        }
        return campiStringa;
    }

    // ...

Invocare un metodo (setter)

public static void setProperty(String name, Object target, Object value) {
    Method metodo = null;
    String nameToUpperCase = checkCase(name);
    try {
        metodo = target.getClass().getMethod("set" + nameToUpperCase, new Class[] {value.getClass()});
    }
    catch (NoSuchMethodException e) { }
    if (metodo != null)
        try {
            metodo.invoke(target, new Object[] {value});
        }
        catch (Exception ecc) { }
}

Dato il nome (name) di un attributo, l’oggetto (target) e nuovo valore dell’attributo (value), individua il metodo adatto (se c’è) e lo invoca.

Invocare un metodo (getter)

public static String getProperty(String name, Object target) {
    String ritorno = new String();
    Method metodo = null;
    String nameToUpperCase = checkCase(name);
    try {
        metodo = target.getClass().getMethod("get" + nameToUpperCase, null);
    }
    catch (NoSuchMethodException exc) { }
    if (metodo != null)
        try {
            ritorno = (String) metodo.invoke(target, new Object[0]);
        }
        catch (Exception ecc) { }
    return ritorno;
    }

From java tutorial

Uses of Reflection

  • Reflection is commonly used by programs which require the ability to examine or modify the runtime behavior of applications running in the Java virtual machine.
  • Should be used only by developers who have a strong grasp of the fundamentals of the language.
  • Reflection is a powerful technique and can enable applications to perform operations which would otherwise be impossible.

Extensibility Features

  • An application may make use of external, user-defined classes by creating instances of extensibility objects using their fully-qualified names.
  • Class Browsers and Visual Development Environments
    • A class browser needs to be able to enumerate the members of classes.
  • Debuggers and Test Tools
    • Debuggers need to be able to examine private members on classes.

Drawbacks of Reflection

  • Reflection is powerful, but should not be used indiscriminately.
  • If it is possible to perform an operation without using reflection, then it is preferable to avoid using it.

Class

Classes

  • For every type of object, the Java virtual machine instantiates an immutable instance of java.lang.Class which provides methods to examine the runtime properties of the object including its members and type information.
  • Class also provides the ability to create new classes and objects.
  • It is the entry point for all of the Reflection APIs.
  • Retrieving Class Objects (ways to get a Class)
  • Examining Class Modifiers and Types (access the class declaration information)
  • Discovering Class Members (list the constructors, fields, methods, and nested classes in a class)

Retrieving Class Objects

Retrieving Class Objects

  • None of the classes in java.lang.reflect have public constructors.
  • To get to these classes, it is necessary to invoke appropriate methods on Class.
  • There are several ways to get a Class depending on whether
    • the code has access to an object
    • the name of class
    • a type
    • an existing Class

Examples

Class c = "foo".getClass();

boolean b;
Class c = b.getClass();   // compile-time error
Class c = boolean.class;  // correct

Class c = Class.forName("it.unipr.AF.MyClass");

Class c = Double.TYPE;

Methods that Return Classes

Class c = javax.swing.JButton.class.getSuperclass();
  • java.lang.reflect.Constructor.getDeclaringClass() Returns the Class in which these members were declared.
import java.lang.reflect.Field;
Field f = System.class.getField("out");
Class c = f.getDeclaringClass();

Examining Class Modifiers and Types

Examining Class Modifiers and Types

  • A class may be declared with one or more modifiers which affect its runtime behavior:
    • Access modifiers: public, protected, and private
    • Modifier requiring override: abstract
    • Modifier restricting to one instance: static
    • Modifier prohibiting value modification: final
    • Modifier forcing strict floating point behavior: strictfp
    • Annotations

Example (1)

import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import static java.lang.System.out;

public class ClassDeclarationSpy {
    public static void main(String... args) {

Example (Class & Modifiers)

    try {
        Class<?> c = Class.forName(args[0]);
        out.format("Class:%n  %s%n%n", c.getCanonicalName());
        out.format("Modifiers:%n  %s%n%n",
               Modifier.toString(c.getModifiers()));

Example (Paramethers)

        out.format("Type Parameters:%n");
        TypeVariable[] tv = c.getTypeParameters();
        if (tv.length != 0) {
              out.format("  ");
              for (TypeVariable t : tv)
                out.format("%s ", t.getName());
                out.format("%n%n");
        } else {
              out.format("  -- No Type Parameters --%n%n");
        }

Example (Implemented Interfaces)

        out.format("Implemented Interfaces:%n");
        Type[] intfs = c.getGenericInterfaces();
        if (intfs.length != 0) {
            for (Type intf : intfs)
                out.format("  %s%n", intf.toString());
            out.format("%n");
        } else {
            out.format("  -- No Implemented Interfaces --%n%n");
        }

Example (Inheritance)

        out.format("Inheritance Path:%n");
        List<Class> l = new ArrayList<Class>();
        printAncestor(c, l);
        if (l.size() != 0) {
            for (Class<?> cl : l)
                out.format("  %s%n", cl.getCanonicalName());
            out.format("%n");
        } else {
              out.format("  -- No Super Classes --%n%n");
        }

Example (Annotations)

        out.format("Annotations:%n");
        Annotation[] ann = c.getAnnotations();
        if (ann.length != 0) {
            for (Annotation a : ann)
                out.format("  %s%n", a.toString());
            out.format("%n");
        } else {
              out.format("  -- No Annotations --%n%n");
        }

Example (1)

        // production code should handle this exception more gracefully
    } catch (ClassNotFoundException x) {
        x.printStackTrace();
    }
    }

Example (printAncestor)

    private static void printAncestor(Class<?> c, List<Class> l) {
    Class<?> ancestor = c.getSuperclass();
    if (ancestor != null) {
        l.add(ancestor);
        printAncestor(ancestor, l);
    }
    }
}

Discovery Class Members

Fields

  • Fields have a type and a value.
  • The java.lang.reflect.Field class provides methods for accessing type information and setting and getting values of a field on a given object.
    • Obtaining Field Types (get the declared and generic types of a field)
    • Retrieving and Parsing Field Modifiers (get portions of the field declaration such as public or transient)
    • Getting and Setting Field Values (access field values)

Methods

  • Methods have return values, parameters, and may throw exceptions.
  • The java.lang.reflect.Method class provides methods for obtaining the type information for the parameters and return value.
  • It may also be used to invoke methods on a given object.
    • Obtaining Method Type Information (enumerate methods declared in a class and obtain type information)
    • Obtaining Names of Method Parameters (retrieve names and other information of a method or constructor's parameters)
    • Retrieving and Parsing Method Modifiers (access and decode modifiers and other information associated with the method)
    • Invoking Methods (execute a method and obtain its return value)

Constructors

  • The Reflection APIs for constructors are defined in java.lang.reflect.Constructor and are similar to those for methods
    • Finding Constructors (retrieve constructors with specific parameters)
    • Retrieving and Parsing Constructor Modifiers (modifiers of a constructor declaration and other information about the constructor)
    • Creating New Class Instances (instantiate an instance of an object by invoking its constructor)

Alberto Ferrari
Ingegneria dell'Informazione, UniPR
www.ce.unipr.it/~aferrari/