¿Puede una clase Java agregar un método a sí misma en tiempo de ejecución?

¿Puede una clase agregar un método a sí misma en el tiempo de ejecución (como desde un bloque static ), de modo que si alguien está realizando una reflexión sobre esta clase, verán el nuevo método, aunque no se haya definido en tiempo de comstackción?

Fondo:

Un framework que estoy usando espera que se definan clases de Action que tengan un doAction(...) , por convención. El marco inspecciona estas clases en tiempo de ejecución para ver qué tipo de parámetros están disponibles en su método doAction() . Por ejemplo: doAction ( String a, Integer b)

Me gustaría que cada clase pueda generar programáticamente su método doAction() con varios parámetros, justo a tiempo cuando se inspecciona. El cuerpo del método puede estar vacío.

No es simple. Una vez que una clase es cargada por un cargador de clases, no hay forma de cambiar los métodos de las clases cargadas. Cuando se solicita una clase, un cargador de clases la cargará y vinculará . Y no hay forma (con Java) de cambiar el código vinculado o agregar / eliminar métodos.

El único truco que se me ocurre es jugar con los cargadores de clases. Si eliminamos un cargador de clases personalizado, las clases cargadas por ese cargador de clases también deberían eliminarse o inaccesibles. La idea que viene a mi mente es

  1. implementar un cargador de clases personalizado
  2. cargar la clase dinámica con ese cargador de clases personalizado
  3. si tenemos una versión actualizada de esta clase,
  4. eliminar el cargador de clases personalizado y
  5. cargar la nueva versión de esta clase con una nueva instancia del cargador de clases personalizado

Lo dejo como algo para pensar , no puedo probar, si esto conduce a una solución o si tenemos dificultades.

Como respuesta simple a la pregunta: No, no podemos cambiar una clase cargada como podemos cambiar el contenido de los campos con reflexión. (no podemos agregar o eliminar campos también).

Andres_D tiene razón, podemos hacerlo utilizando carga de clase personalizada, aquí hay una guía detallada sobre cómo hacer esto: http://www.javaworld.com/javaworld/jw-06-2006/jw-0612-dynamic. html? page = 1

El artículo explica cómo escribir código Java dynamic. Discute la comstackción de código fuente de tiempo de ejecución, la recarga de clase y el uso del patrón de diseño Proxy para hacer que las modificaciones de una clase dinámica sean transparentes para quien llama.

De hecho, un investigador en Austria ha escrito una JVM que incluso permite volver a cargar clases con diferentes jerarquías de tipos. Lo han logrado mediante el uso de puntos de guardado de hilos existentes para generar un “universo lateral” completo de un objeto y todas sus referencias y contenido referenciado, y luego, una vez completamente reorganizados con todos los cambios requeridos, simplemente intercambian todas las clases modificadas. [1] Aquí un enlace a su proyecto http://ssw.jku.at/dcevm/ el patrocinio del oracle ciertamente hace especulaciones interesantes sobre planes futuros.

Los cambios menos intrusivos en los cuerpos y campos de métodos ya son posibles en la máquina virtual estándar de Java que utiliza las capacidades de Intercambio en Caliente de la JPDA tal como se introdujo en Java 1.4:
docs.oracle.com/javase/1.4.2/docs/guide/jpda/enhancements.html#hotswap

No estoy seguro de si fue el primero, pero el documento de este empleado de 2001 parece ser una de las primeras propuestas que menciona las capacidades del HotSpot para Hot Swap. [2]

REFERENCIA

[1] T. Würthinger, C. Wimmer y L. Stadler, “Dynamic Code Evolution for Java”, presentado en la 8ª Conferencia Internacional sobre los Principios y la Práctica de la Progtwigción en Java, Viena, 2010.

[2] M. Dmitriev, “Hacia una tecnología flexible y segura para la evolución del tiempo de ejecución de las aplicaciones del lenguaje Java”, en el Taller de OOPSLA sobre Ingeniería de sistemas orientados a objetos complejos para la evolución, 2001.

Nunca he probado algo así, pero deberías echarle un vistazo a ASM , cglib y Javassist .

No, eso no es (fácilmente) posible en Java.

Parece que está intentando usar Java como si fuera un lenguaje de progtwigción dynamic. Por ejemplo, Ruby tiene clases abiertas: puede agregar y eliminar métodos de las clases de Ruby en tiempo de ejecución. En Ruby, también puede tener un método “método faltante” en su clase, que será invocado cuando intente llamar a un método que no existe en la clase. Tal cosa tampoco existe en Java.

Hay una versión de Ruby que corre en JVM, JRuby, y tiene que hacer trucos muy difíciles para hacer que las clases abiertas funcionen en la JVM.

Puede tener un método doAction que hace lo que le gustaría que hiciera el método generado. ¿Hay alguna razón por la que deba generarse o puede ser dinámica?

Creo que necesitas alguna herramienta / marco de modificación de código de bytes, como asm, cglib o javassist. Puede lograr esto a través de aspectos / tejido como lo hizo Spring, pero creo que todavía necesita tener el método definido primero.

Proxy puede ayudar. Pero debe instanciar un Proxy cada vez que quiera agregar o eliminar un método.

Lo que sugiero debería funcionar para su situación: 1. Tiene una clase existente MyClass con n métodos 2. Desea incluir (n + 1) el método que no está en la clase mientras comstack en otro archivo fuente .java

Mi manera de resolverlo es Herencia. Cree un nuevo archivo fuente .java para una clase MyClassPlusOne extendiendo la clase MyClass de primera clase. Comstack esta clase y usa el objeto. ¿Cómo puedo comstackr e implementar una clase java en tiempo de ejecución?

 class MyClassPlusOne extends MyClass { void doAction(String a, Integer b) { int myNPlus1 = a+b; //add whatever you want before compiling this code } } 

No estoy seguro de que eso sea posible. Sin embargo, puede usar AspectJ, ASM, etc. y tejer estos métodos en las clases apropiadas.

La otra alternativa es usar composición para envolver la clase objective y proporcionar el método doAction. En este caso, terminarías delegando en la clase objective.

Parece que no hay forma de agregar el método dinámicamente. Pero puedes preparar una clase con una lista de Métodos o un hash como:

 import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; public class GenericClass { private HashMap methodMap = new HashMap(); public Object call(String methodName,Object ...args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { Method method = methodMap.get(methodName); return method.invoke(null, args); } public void add(String name,Method method){ if(Modifier.isStatic(method.getModifiers())) methodMap.put(name, method); } public static void main(String[] args) { try { GenericClass task = new GenericClass(); task.add("Name",Object.class.getMethod("Name", new Class[0])); } catch (NoSuchMethodException | SecurityException e) { e.printStackTrace(); } } } 

Entonces, usando reflexiones puedes establecer o deshacer el atributo.

    Intereting Posts