2010-02-07 10 views
17

Todos los ejemplos que miro para la reflexión muestran la creación de una nueva instancia de una implementación desconocida y el envío de esa implementación a su interfaz. El problema con esto es que ahora no puede llamar a ningún método nuevo (solo reemplaza) en la clase de implementación, ya que su variable de referencia de objeto tiene el tipo de interfaz. Aquí es lo que tengo:¿Usar la reflexión en Java para crear una nueva instancia con el tipo de variable de referencia establecido en el nuevo nombre de clase de instancia?

Class c = null; 
try { 
    c = Class.forName("com.path.to.ImplementationType"); 
} catch (ClassNotFoundException e) { 
    e.printStackTrace(); 
} 
InterfaceType interfaceType = null; 
try { 
    interfaceType = (InterfaceType)c.newInstance(); 
} catch (InstantiationException e) { 
    e.printStackTrace(); 
} catch (IllegalAccessException e) { 
    e.printStackTrace(); 
} 

Si sólo tengo una referencia a "com.path.to.ImplementationType", y yo no sé lo que ese tipo podría ser (que viene de un archivo de configuración) , entonces, ¿cómo puedo usar el nombre de clase para convertirlo a ImplementationType? ¿Esto es posible?

Respuesta

28

Esta línea parece resumir el punto crucial de su problema:

El problema con esto es que ahora no se puede llamar a cualquier nuevos métodos (sólo overrides) en el implementar clase, ya que su variable de referencia de objeto tiene el tipo de interfaz.

Estás bastante atrapado en tu implementación actual, ya que no solo tienes que intentar un elenco, también necesitas la definición de los métodos que deseas invocar en esta subclase. Veo dos opciones:

1. Como se indica en otra parte, no puede usar la representación de cadena del nombre de clase para convertir su instancia reflejada a un tipo conocido. Puede, sin embargo, utilizar una prueba Stringequals() para determinar si su clase es del tipo que desea, y luego realizar un hard-coded reparto:

try { 
    String className = "com.path.to.ImplementationType";// really passed in from config 
    Class c = Class.forName(className); 
    InterfaceType interfaceType = (InterfaceType)c.newInstance(); 
    if (className.equals("com.path.to.ImplementationType") { 
     ((ImplementationType)interfaceType).doSomethingOnlyICanDo(); 
    } 
} catch (Exception e) { 
    e.printStackTrace(); 
} 

Esto se ve muy feo, y arruina el buen config conducido por usted que tiene. No sugiero que hagas esto, es solo un ejemplo.

2. Otra opción que tienes es extender su reflexión desde sólo Class/Object creación de incluir Method reflexión. Si puede crear el Class desde un String pasado desde un archivo de configuración, también puede pasar un nombre de método desde ese archivo de configuración y, a través de la reflexión, obtener una instancia del propio Method desde su objeto Class. A continuación, puede llamar al invoke (http://java.sun.com/javase/6/docs/api/java/lang/reflect/Method.html#invoke(java.lang.Object, java.lang.Object ...)) en el Method, pasando la instancia de su clase que creó. Creo que esto te ayudará a obtener lo que buscas.

Aquí hay algunos códigos para que sirvan de ejemplo. Tenga en cuenta que me he tomado la libertad de codificar los parámetros para los métodos. Podrías especificarlos en una configuración también, y necesitaría reflejar en sus nombres de clase para definir sus obejcts e instancias Class.

public class Foo { 

    public void printAMessage() { 
    System.out.println(toString()+":a message"); 
    } 
    public void printAnotherMessage(String theString) { 
     System.out.println(toString()+":another message:" + theString); 
    } 

    public static void main(String[] args) { 
     Class c = null; 
     try { 
      c = Class.forName("Foo"); 
      Method method1 = c.getDeclaredMethod("printAMessage", new Class[]{}); 
      Method method2 = c.getDeclaredMethod("printAnotherMessage", new Class[]{String.class}); 
      Object o = c.newInstance(); 
      System.out.println("this is my instance:" + o.toString()); 
      method1.invoke(o); 
      method2.invoke(o, "this is my message, from a config file, of course"); 
     } catch (ClassNotFoundException e) { 
      e.printStackTrace(); 
     } catch (NoSuchMethodException nsme){ 
      nsme.printStackTrace(); 
     } catch (IllegalAccessException iae) { 
      iae.printStackTrace(); 
     } catch (InstantiationException ie) { 
      ie.printStackTrace(); 
     } catch (InvocationTargetException ite) { 
      ite.printStackTrace(); 
     } 
    } 
} 

y mi salida:

this is my instance:[email protected] 
[email protected]:a message 
[email protected]:another message:this is my message, from a config file, of course 
+0

¿Dónde puedo descargar el tarro de los métodos '' InterfaceType' y ImplementationType'? –

0

Si conocía el Class de ImplementationType, podría crear una instancia del mismo. Entonces, lo que estás tratando de hacer no es posible.

1

¿Quieres ser capaz de pasar una clase y obtener una instancia de tipo seguro de esa clase? Pruebe lo siguiente:

public static void main(String [] args) throws Exception { 
    String s = instanceOf(String.class); 
} 

public static <T> T instanceOf (Class<T> clazz) throws Exception { 
    return clazz.newInstance(); 
} 
1

No estoy absolutamente seguro de que me dieron bien su pregunta, pero parece que desea algo como esto:

Class c = null; 
    try { 
     c = Class.forName("com.path.to.ImplementationType"); 
    } catch (ClassNotFoundException e) { 
     e.printStackTrace(); 
    } 
    T interfaceType = null; 
    try { 
     interfaceType = (T) c.newInstance(); 
    } catch (InstantiationException e) { 
     e.printStackTrace(); 
    } catch (IllegalAccessException e) { 
     e.printStackTrace(); 
    } 

Donde T puede ser definido en el nivel de método o en el nivel de clase, es decir, <T extends InterfaceType>

2

Como una adición a la respuesta de akf podría usar verificaciones instanceof en lugar de String equals() llamadas:

String cname="com.some.vendor.Impl"; 
try { 
    Class c=this.getClass().getClassLoader().loadClass(cname); 
    Object o= c.newInstance(); 
    if(o instanceof Spam) { 
    Spam spam=(Spam) o; 
    process(spam); 
    } 
    else if(o instanceof Ham) { 
    Ham ham = (Ham) o; 
    process(ham); 
    } 
    /* etcetera */ 
} 
catch(SecurityException se) { 
    System.err.printf("Someone trying to game the system?%nOr a rename is in order because this JVM doesn't feel comfortable with: “%s”", cname); 
    se.printStackTrace(); 
} 
catch(LinkageError le) { 
    System.err.printf("Seems like a bad class to this JVM: “%s”.", cname); 
    le.printStackTrace(); 
} 
catch(RuntimeException re) { 
    // runtime exceptions I might have forgotten. Classloaders are wont to produce those. 
    re.printStackTrace(); 
} 
catch(Exception e) { 
    e.printStackTrace(); 
} 

Tenga en cuenta la codificación física liberal de algunos valores. De todos modos, los puntos principales son:

  1. Use instanceof en lugar de equals(). En todo caso, cooperará mejor al refactorizar.
  2. Asegúrese de detectar estos errores de tiempo de ejecución y de seguridad también.
3
//====Single Class Reference used to retrieve object for fields and initial values. Performance enhancing only====   
Class<?> reference   = vector.get(0).getClass(); 
Object  obj     = reference.newInstance(); 
Field[]  objFields   = obj.getClass().getFields(); 
+1

Así es como resolví la necesidad de recuperar información para crear una nueva instancia de una clase desconocida para usar sus campos para otra búsqueda genérica (por campo/valor yo filtros). Olvide el comentario de "rendimiento", tiene algo que ver con unas pocas líneas después de estos. – GoToJack

Cuestiones relacionadas