简体   繁体   中英

Force explicit method resolution in ByteBuddy MethodDelegation to allow use of lambdas

I'm using ByteBuddy to implement at run-time a marker interface and an arbitrary number of accessor-like no-argument value methods, identified by a marker annotation, something like:

interface Foo {
  // marker interface
}

// this is the kind of thing we're generating implementations of
interface MyFoo extends Foo {
  @Value
  SomeClass bar();
}

I have a functional interface for the implementation delegate, something like:

interface Implementation<F extends Foo, V> {
  @RuntimeType
  V apply(@This F foo);
}

and factories that produce the actual implementations. The implementation involves a variety of generics, wildcards, and unchecked (but known to be safe) casts that make it hard to get the exact type arguments right.

interface ImplFactory<V> {
  boolean canImplement(Method m);
  <F extends Foo> Implementation<F, ?> implFor(Method m);
}

class Factories {
  static <F extends Foo> Implementation<F, ?> implFor(Method m) {
    ImplFactory<?> factory = factories.find((f) -> canImplement(m))
    return factory.implFor(m);
  }
}

Implementation<F, ?> impl = Factories.implFor(m);
builder.method(ElementMatchers.is(m)).intercept(MethodDelegation.to(impl));

If I use lambdas, ByteBuddy complains that it can't find any matching method:

class SomeFactory implements ImplFactory<?XYZ> {
  <F extends Foo> Implementation<F, ?XYZ> implFor(Method m) {
    return (f) -> /* ...lookup & runtime cast shenanigans... */
  }
} 

(For ?XYZ , understand some combination of parameterized types and wildcards.)

This is true despite Implementation.apply() being annotated with @RuntimeType and @This -- possibly because at runtime ByteBuddy can't tell that the lambda is an Implementation ? -- and persists even if I add a @This to the lambda:

class SomeFactory implements ImplFactory<?XYZ> {
  <F extends Foo> Implementation<F, ?XYZ> implFor(Method m) {
    // still doesn't work
    return (@This F f) -> /* ...shenanigans... */
  }
} 

However, if I expand the lambda to an abstract class and re-annotate, it works:

class SomeFactory implements ImplFactory<?XYZ> {
  <F extends Foo> Implementation<F, ?XYZ> implFor(Method m) {
      return new Implementation<F, ?XYZ>() {
          @Override
          @RuntimeType
          ?XYZ apply(@This F foo) {
              /* ...shenanigans... */
          }
      }
  }
} 

What I really want to do is just tell ByteBuddy "just delegate to this object, and just delegate to the apply() method -- trust me, it works!" But there doesn't seem to be any way to do that.

How can I force ByteBuddy to use a specific implementation method instead of trying to do a smart lookup?

Method overrides do not really exist at runtime. Annotations on methods, fields or interfaces are not inherited for many reasons and Byte Buddy does not scan the inheritance hierarchy for getting the job done.

I would recommend you to use a delegate class for the instrumentation that has the annotations and takes the actual target as an argument to resolve this.

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