Can a Java class add a method to itself at runtime?

JavaReflectionCode GenerationJavassist

Java Problem Overview


Can a class add a method to itself at runtime (like from a static block), so that if someone is performing reflection on this class, they'll see the new method, even though it wasn't defined at compile time?

Background:

A framework I'm using expects Action classes to be defined that have a doAction(...) method, by convention. The framework inspects these classes at runtime to see what type of parameters are available in their doAction() method. For example: doAction(String a, Integer b)

I'd like each class to be able to programatically generate its doAction() method with various parameters, just-in-time when it is inspected. The body of the method can be empty.

Java Solutions


Solution 1 - Java

It's not simple. Once a class is loaded by a classloader, there is no way to change the methods of loaded classes. When a class is requested, a classloader will load it and link it. And there is no way (with Java) to change the linked code or to add/remove methods.

The only trick that comes to my mind is playing with classloaders. If we delete a custom classloader, then the classes loaded by that classloader should be deleted or inaccessible too. The idea that comes to my mind is to

  1. implement one custom classloader
  2. load the dynamic class with that custom classloader
  3. if we have an updated version of this class,
  4. remove the custom classloader and
  5. load the new version of this class with a new instance of the custom classloader

I leave that as food for thought, can't prove, if this leads to a solution or if we have pitfalls.

As a simple answer to the question: No, we can't change a loaded class like we can change the content of fields with reflection. (we can't add or remove fields too).

Solution 2 - Java

Andres_D is right, we can very well do so using custom class loading, here is a detailed guide on how to do this: http://www.javaworld.com/javaworld/jw-06-2006/jw-0612-dynamic.html?page=1

> The article explains how to write dynamic Java code. It discusses runtime source code compilation, class reloading, and the use of the Proxy design pattern to make modifications to a dynamic class transparent to its caller.

In fact researcher in Austria have written a JVM that even allows reloading classes with different type hierarchies. They have achieved this by using existing thread save points to generate a complete 'side universe' of an object and all it's related references and referenced content and then once fully reshuffled with all required changes simply swap in all changed classes. [1] Here a link to their project http://ssw.jku.at/dcevm/ the oracle sponsorship certainly makes for interesting speculations on future plans.

Less intrusive changes to method bodies and fields are already possible in the standard java VM using the Hot Swap capabilities of the JPDA as introduced in Java 1.4:
docs.oracle.com/javase/1.4.2/docs/guide/jpda/enhancements.html#hotswap

I'm not sure whether it was the first one but this Sun employee's paper from 2001 appears to be one of the early proposals mentioning the capabilities of the HotSpot to Hot Swap. [2]

REFERENCE

[1] T. Würthinger, C. Wimmer, and L. Stadler, “Dynamic Code Evolution for Java,” presented at the 8th International Conference on the Principles and Practice of Programming in Java, Vienna, 2010.

[2] M. Dmitriev, “Towards flexible and safe technology for runtime evolution of java language applications,” in OOPSLA Workshop on Engineering Complex Object-Oriented Systems for Evolution, 2001.

Solution 3 - Java

I've never tried anything quite like that myself, but you should have a look at ASM, cglib, and Javassist.

Solution 4 - Java

No, that is not (easily) possible in Java.

It sounds like you are trying to use Java as if it is a dynamic programming language. For example, Ruby has open classes: you can add and remove methods from Ruby classes at runtime. In Ruby, you can also have a "method missing" method in your class, that will be called when you try to call a method that doesn't exist in the class. Such a thing also doesn't exist in Java.

There is a version of Ruby that runs on the JVM, JRuby, and it has to do very difficult tricks to make open classes work on the JVM.

Solution 5 - Java

You can have a doAction method which does whatever you would like the generated method to do. Is there a reason it needs to be generated or can it be dynamic?

Solution 6 - Java

It looks like there is no way to add method dynamically. But you can prepare an class with a list of Methods or an hash like:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;

public class GenericClass {
	private HashMap<String, Method> methodMap = new HashMap<String, Method>();
	
	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();
        }
   }
}

Than, using reflections you can set or unset the attribute.

Solution 7 - Java

I believe you need some byte code altering tool/framework, such as asm, cglib or javassist. You can achieve this via aspects/weaving like it's done Spring, but I believe you still need to have the method defined first.

Solution 8 - Java

Proxy may help. But have to instantiate a Proxy every time you want to add or remove a method.

Solution 9 - Java

What I suggest should work for your situation:

  1. You have an existing class MyClass with n methods
  2. You want to include (n+1) th method which is not in the class while compiling in another .java source file

My way to solve it is Inheritance. Create a new .java source file for a Class MyClassPlusOne extending the first class MyClass. Compile this class and use the object. https://stackoverflow.com/questions/1064259/how-can-i-compile-and-deploy-a-java-class-at-runtime

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

Solution 10 - Java

I'm not sure that is possible. However, you could use AspectJ, ASM, etc. and weave these methods into the appropriate classes.

The other alternative is to use composition to wrap the target class and provide the doAction method. You would end up delegating to the target class in this case.

Solution 11 - Java

This is a rather old question, but I still found myself looking at it today so, just in case, I'll add my two cents.

If you are using Java 8+, you can define "default" implementations of an interface method, so you can just define the interface with all the extra methods with empty default implementations, and add the implements clause in the desired classes. This approach, in some cases, may be the easiest one.

If you don't have control over the definition of the classes, or you need compatibility with older Java versions, you can still define an interface containing all the required extra methods; but in this case, implement a "Decorator" class with a method that receives the object to "decorate" as parameter, and returns a DynamicProxy instance, wrapping the passed object with this interface.

If you are using Spring, the decorator can be added to the context as a @Component, so you can inject it wherever you need to use it. If any of the objects you need to inject are Spring Beans, you could implement a FactoryBean that uses the decorator to return the instances, so you can just forget about calling the decorator explicitly for them.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionslatteryView Question on Stackoverflow
Solution 1 - JavaAndreas DolkView Answer on Stackoverflow
Solution 2 - JavaDrGranitView Answer on Stackoverflow
Solution 3 - JavaRyan StewartView Answer on Stackoverflow
Solution 4 - JavaJesperView Answer on Stackoverflow
Solution 5 - JavaPeter LawreyView Answer on Stackoverflow
Solution 6 - JavaCalebe Elias Ribeiro BrimView Answer on Stackoverflow
Solution 7 - JavacarlspringView Answer on Stackoverflow
Solution 8 - JavaBokiView Answer on Stackoverflow
Solution 9 - Javauser9721802View Answer on Stackoverflow
Solution 10 - Javatjg184View Answer on Stackoverflow
Solution 11 - JavaNodens2kView Answer on Stackoverflow