2008-10-07 18 views
44

Necesito hacer algunas llamadas a métodos reflexivos en Java. Esas llamadas incluirán métodos que tienen argumentos que son tipos primitivos (int, double, etc.). La forma de especificar dichos tipos al buscar el método de forma reflexiva es int.class, double.class, etc.Encuentra dinámicamente la clase que representa un tipo Java primitivo

El desafío es que estoy aceptando la entrada desde una fuente externa que especificará los tipos dinámicamente. Por lo tanto, también necesito presentar estas referencias de Clase dinámicamente. Imagine un archivo delimitado por una lista de nombres de los métodos con las listas de tipos de parámetros:

doSomething int double 
doSomethingElse java.lang.String boolean 

Si la entrada era algo así como java.lang.String, sé que podría utilizar Class.forName("java.lang.String") a esa instancia Clase espalda. ¿Hay alguna forma de utilizar ese método, u otro, para recuperar las Clases de tipo primitivo?

Editar: Gracias a todos los encuestados. Parece claro que no hay una forma integrada de hacer lo que quiero, así que me conformaré con reutilizar la clase ClassUtils del framework Spring. Parece contener un reemplazo para Class.forName() que funcionará con mis requisitos.

Respuesta

18

El marco Spring contiene una clase de utilidad ClassUtils que contiene el método forName estática. Este método se puede utilizar para el propósito exacto que describió.

En caso de que no desee tener una dependencia en Spring: se puede encontrar source code of the method e. gramo. here en su repositorio público. El código fuente de la clase tiene licencia bajo el modelo Apache 2.0.

Sin embargo, tenga en cuenta que el algoritmo utiliza un mapa codificado de tipos primitivos.


Editar: Gracias a comentaristas David Horvath y Patrick para señalar el enlace roto.

+4

La fuente del archivo ClassUtils.java parece estar rota en ese enlace. [Este me mira, a mí] (http://www.jarvana.com/jarvana/view/org/springframework/spring-core/3.1.0.RELEASE/spring-core-3.1.0.RELEASE-sources. jar! /org/springframework/util/ClassUtils.java? format = ok) – Patrick

+1

@Patrick: lamentablemente, tu enlace se ha roto también. Sin embargo, Spring framework ya está disponible en GitHub. https://github.com/spring-projects/spring-framework/blob/master/spring-core/src/main/java/org/springframework/util/ClassUtils.java –

23

Las instancias de Class para los tipos primitivos se pueden obtener como usted dijo usando p. Ej. int.class, pero también es posible obtener los mismos valores usando algo como Integer.TYPE. Cada clase de envoltura primitiva contiene un campo estático, TYPE, que tiene la instancia de clase primitiva correspondiente.

No puede obtener la clase primitiva a través de forName, pero puede obtenerla de una clase que esté disponible. Si es absolutamente necesario utilizar la reflexión, se puede intentar algo como esto:

Class clazz = Class.forName("java.lang.Integer"); 
Class intClass = clazz.getField("TYPE").get(null); 

intClass.equals(int.class);   // => true 
+0

Esto es cierto, y yo no estaba al tanto. Sin embargo, en mi situación, necesito una forma de especificar que cualquier método se pueda llamar de manera reflexiva. Por lo tanto, si acepto java.lang.Integer para representar valores int, perderé la capacidad de llamar a métodos que acepten valores enteros. –

+0

Puede simplemente probar para ver si ha recibido algún tipo especial arbitrario (digamos: "int") y luego reescribir apropiadamente. No hay ningún valor de cadena que pueda ingresar forName que recuperará la clase de un tipo primitivo. –

+0

Ok, gracias. Su respuesta fue útil. –

16

Probablemente sólo tiene que asignar las primitivas y para el resto de las clases de realizar el método "forName":

me gustaría hacer algo como:

void someWhere(){ 
    String methodDescription = "doSomething int double java.lang.Integer java.lang.String" 
    String [] parts = methodDescription.split(); 
    String methodName= parts[0] 
    Class [] paramsTypes = getParamTypes(parts); // Well, not all the array, but a, sub array from 1 to arr.length.. 

    Method m = someObject.class.getMethod(methodName, paramTypes); 
    etc. etc etc. 
} 

public Class[] paramTypes(String [] array){ 
    List<Class> list = new ArrayList<Class>(); 
    for(String type : array) { 
     if(builtInMap.contains(type)) { 
      list.add(builtInMap.get(type)); 
      }else{ 
      list.add(Class.forName(type)); 
      } 
    } 
    return list.toArray(); 
} 

    // That's right. 
Map<String,Class> builtInMap = new HashMap<String,Class>();{ 
     builtInMap.put("int", Integer.TYPE); 
     builtInMap.put("long", Long.TYPE); 
     builtInMap.put("double", Double.TYPE); 
     builtInMap.put("float", Float.TYPE); 
     builtInMap.put("bool", Boolean.TYPE); 
     builtInMap.put("char", Character.TYPE); 
     builtInMap.put("byte", Byte.TYPE); 
     builtInMap.put("void", Void.TYPE); 
     builtInMap.put("short", Short.TYPE); 
} 

Es decir, crear un mapa donde los tipos de primitivas se almacenan y si la descripción pertenece a una primitiva, entonces use la clase mapeada. Este mapa también se puede cargar desde un archivo de configuración externo, para agregar flexibilidad para que agregue String como una función incorporada en lugar de java.lang.String o potencialmente tenga un método como este.

"cadena doSomething sí | no"

Hay un montón de este tipo de código en proyectos OS como libs Struts, Hibernate, Spring and Apache (por mencionar algunos), por lo que no es necesario empezar desde cero.

BTW. No compilé el código anterior, pero estoy bastante seguro de que funciona con pequeñas modificaciones, no me vote por eso.

+0

Esto es lo que se hace dentro de ObjectInputStream también , simplemente realiza una búsqueda ForName() y si arroja una ClassNotFoundException, realiza una búsqueda en el mapa. – Robin

+0

Gracias por la respuesta. Reutilizaré el método de utilidad en ClassUtils de Spring que usa este proceso de búsqueda. –

+1

¿Dónde está usando este código de "bajo nivel"? Eche un vistazo profundo sobre cómo proteger su servicio, de lo contrario, podría obtener una llamada "System.exit()" de su cliente remoto, ¡y créame que el servidor se apagará! – OscarRyz

3

Desafortunadamente, varios métodos de Clase no manejan primitivas de manera consistente. Una forma común de esto en forName es tener una tabla como;

private static final Map<String, Class> BUILT_IN_MAP = 
    new ConcurrentHashMap<String, Class>(); 

static { 
    for (Class c : new Class[]{void.class, boolean.class, byte.class, char.class, 
      short.class, int.class, float.class, double.class, long.class}) 
     BUILT_IN_MAP.put(c.getName(), c); 
} 

public static Class forName(String name) throws ClassNotFoundException { 
    Class c = BUILT_IN_MAP.get(name); 
    if (c == null) 
     // assumes you have only one class loader! 
     BUILT_IN_MAP.put(name, c = Class.forName(name)); 
    return c; 
} 
2

El siguiente código explica cómo obtener la clase de un tipo primitivo cuyo nombre de campo se conoce, p. Ej. en este caso 'sampleInt'.

public class CheckPrimitve { 
    public static void main(String[] args) { 
     Sample s = new Sample(); 
     try { 
      System.out.println(s.getClass().getField("sampleInt").getType() == int.class); // returns true 
      System.out.println(s.getClass().getField("sampleInt").getType().isPrimitive()); // returns true 
     } catch (NoSuchFieldException e) {   
      e.printStackTrace(); 
     } catch (SecurityException e) { 
      e.printStackTrace(); 
     }  
    } 
} 

class Sample { 
    public int sampleInt; 
    public Sample() { 
     sampleInt = 10; 
    } 
} 

Uno puede también comprobar si un valor dado es primitivo o no consiguiendo su respectivo contenedor de clase o de su valor de campo.

public class CheckPrimitve { 
     public static void main(String[] args) { 
      int i = 3; 
      Object o = i; 
      System.out.println(o.getClass().getSimpleName().equals("Integer")); // returns true 
      Field[] fields = o.getClass().getFields(); 
      for(Field field:fields) { 
       System.out.println(field.getType()); // returns {int, int, class java.lang.Class, int} 
      } 
     } 
    } 
+0

¡Gracias por publicar una respuesta! Si bien un fragmento de código podría responder a la pregunta, sigue siendo genial agregar información adicional, como explicar, etc. – j0k

+0

@ j0k He agregado una explicación al código. Por favor, compruebe. – Arham

+0

esto es mucho mejor, gracias – j0k

0

Google Guava ofrece com.google.common.primitives.Primitives para este tipo de cosas.

Cuestiones relacionadas