简体   繁体   中英

Method delegation on field and cast in Bytebuddy

Using Bytebuddy assume the following:

class Service {
    protected Handler handler;
...
}

interface Handler {
   public void handle();
}

class ConcreteHandler implements Handler {
    public void handle() {
       ...
    }
    public void handle2() {
       ...
    }
}

Generally we call the handle() method of Handler, but in some cases we need to call handle2() of ConcreteHandler's class which is it in runtime. Now the question: is that possible using Bytebuddy? Probably yes, but how?

i tried that:

if (condition) {
    MethodCall methodCall = (MethodCall) 
        MethodCall.invoke(methodHandle2).onField("handler")
                        .withAssigner(Assigner.DEFAULT, Assigner.Typing.DYNAMIC);
}

... but it fails with an IllegalStateException: Cannot invoke public void com.framework.ConcreteHandler.handle2(...) on protected com.framework.Handler com.framework.Service.handler

Any ideas?

This seems to be a bug in Byte Buddy. I hopefully fixed the issue in this commit which should resolve the issue in Byte Buddy 1.10.14. You can try the snapshot in the meantime.

If your field variable is a Handler , you cannot call handle2() , since Handler's don't know about the handle2() method.

In order to call the handle2() method (for a valid class), you'd need to cast the handler to a ConcreteHandler - a process known as "downcasting" as you're casting the instance to an object lower down in the object hierarchy. This only works when the handler instance is actually a ConcreteHandler rather than some other derivation of the Handler .

example:

if (handler instanceof ConcreteHandler) {
   ConcreteHandler concreteHandler = (ConcreteHandler) handler;
   concreteHandler.handle2();
}

EDIT: The one-off redirect I got working. I'm not entirely certain how you'd get it working as a permanent redirect from the docs but it would seem like redefine would work if you wanted to permanently redirect the method. There was also a change to the source necessary to make the classes ConcreteHandler, Handler, etc, public.

 new ByteBuddy()
                .subclass(ConcreteHandler.class)
                .method(named("handle")).intercept(MethodDelegation.to(ConcreteHandler.class))
                .make()
                .load(getClass().getClassLoader())
                .getLoaded()
                .newInstance()
                .handle2();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM