How to change method behaviour through reflection?
Asked Answered
M

3

35

I have a a static method in some legacy code, which is called by multiple clients. I obviously have no options to override it, or change behaviour through dependency injection. I am not allowed to modify the existing class.

What I want to do now is change the behaviour (that method - with the same signature and return type) using reflection.

Is it possible ? If not, can any design pattern rescue me ?

Thanks !

EDIT : There is some confusion on what can I change/modify. I cannot change any existing class/method - but I can add more classes to the project. The best I can do with the existing classes is annotate them. This is all done to avoid breaking anything in the existing code - which means a complete round of testing for a big project.

EDIT 2 : java.lang.Instrumentation is not available for Android - or else it sounds like a good fit !

Mac answered 12/4, 2013 at 8:38 Comment(5)
You are not allowed to change it, but you are allowed to modify it via reflection? That seems way more risky.Teacake
I think this is going to end up in a maintenance nightmare. The code will be lying at anybody reading it. Go and get the approval to change that class.Hemorrhage
@Hemorrhage : I understand this is tricky - we have mechanism to alert the future teams of this. That's why I got the approval.Mac
AOP, aspect oriented programming might do just that, wrapping its call.Karikaria
Your are changing behavior in any case and this needs to be tested. So testing effort is always the same. The only thing is how you implement that change.Hemorrhage
T
37

Sounds like a weird requirement...

Anyway, reflection does not allow you to change code behaviour, it can only explore current code, invoke methods and constuctors, change fields values, that kind of things.

If you want to actually change the behaviour of a method you would have to use a bytecode manipulation library such as ASM. But this will not be very easy, probably not a good idea...

Patterns that might help you :

  • If the class is not final and you can modify the clients, extend the existing class and overload the method, with your desired behaviour. Edit : that would work only if the method were not static !
  • Aspect programming : add interceptors to the method using AspectJ

Anyway, the most logical thing to do would be to find a way to modify the existing class, work-arounds will just make your code more complicated and harder to maintain.

Good luck.

Tibold answered 12/4, 2013 at 8:50 Comment(3)
The method being static cannot be overridden. I am wondering if reflection can change field values, why not methods !Mac
I am new to AOP. Exploring it now !Mac
You're right about the method being static of course. I edited my answer accordingly.Tibold
C
26

I guess you could have a look at Instrumentation class which have a method redefineClasses(ClassDefintion classDefinition).

The redefinition may change method bodies, the constant pool and attributes. The redefinition must not add, remove or rename fields or methods, change the signatures of methods, or change inheritance.

Hope this helps.

References: Javadoc

Chantel answered 12/4, 2013 at 8:54 Comment(1)
Sorry, forgot to add this is Android. I don't have this class to avail :(Mac
D
3

You can change method behaviour via Java's dynamic proxies mechanism. See this guide. It will proxied all object methods. You can redefine only some methods by method name, like:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (method.getName().equals("put")) { // example for map
        methods.get(method.getName()).invoke(target, args);
        args[0] = "second"; // put again with "second" key
        Object result = methods.get(method.getName()).invoke(target, args);

        return result;
    }

    if (method.getName().equals("get")) { // example for map
        System.out.println("Method get");  // you implementation
        return methods.get(method.getName()).invoke(target, args);
    }

    return methods.get(method.getName()).invoke(target, args); // just do what initial method do
}
Destructor answered 24/3, 2022 at 10:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.