Search code examples
javabyte-buddy

Is an Assigner.Typing.DYNAMIC Assigner required when returning a subtype?


Suppose I wish to implement the Function<Object, Object> interface by doing something very stupid like this:

final ParameterizedType whatTheUserWillSee = functionObjectObject(); // e.g. Function<Object, Object>
DynamicType.Builder<?> builder = builder
  .subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)
      .merge(Visibility.PUBLIC, SyntheticState.SYNTHETIC, TypeManifestation.FINAL)
      .defineConstructor(Visibility.PUBLIC, SyntheticState.SYNTHETIC)
      .intercept(MethodCall.invoke(OBJECT_CONSTRUCTOR).onSuper()) // OBJECT_CONSTRUCTOR is just new Object()
      .implement(whatTheUserWillSee)
      .intercept(MethodCall.invoke(new MethodDescription.ForLoadedMethod(Object.class.getMethod("toString"))
                 .withAssigner(Assigner.DEFAULT, Assigner.Typing.DYNAMIC));

That is, I'm creating something like this:

public Foo() {
  super();
}
public Object apply(Object x) {
  return toString();
}

Without the .withAssigner invocation, I get:

java.lang.IllegalStateException: Cannot return class java.lang.String from public java.util.function.Function java.util.function.Function.compose(java.util.function.Function)
    at net.bytebuddy.implementation.MethodCall$TerminationHandler$Simple$1.toStackManipulation(MethodCall.java:3005)
    at net.bytebuddy.implementation.MethodCall$Appender.toStackManipulation(MethodCall.java:3551)
    at net.bytebuddy.implementation.MethodCall$Appender.apply(MethodCall.java:3509)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyCode(TypeWriter.java:708)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyBody(TypeWriter.java:693)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod.apply(TypeWriter.java:600)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation.create(TypeWriter.java:5660)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:2166)
    at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:232)
    at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:204)
    at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:3595)

I am not an expert in the internals of ByteBuddy. Why is a dynamic type assigner necessary in order to return a subtype of Object?


Solution

  • You are intercepting all methods of Function. While you think that you are intercepting apply, if you look carefully, Byte Buddy is complaining about the compose method. Specify an explicit matcher to avoid this.