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

二次信任 提交于 2019-11-26 11:20:27
Andreas_D

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).

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.

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

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.

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?

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.

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

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. 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
      }
}

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.

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!