Search code examples
javabyte-buddy

ByteBuddy cannot cast generic return type


I am currently writing a maven plugin that supplies a given library with enhanced getters for all properties that carry a particular annotation as a marker.

@Reference(name="address", targetType=Address.class)
protected String addressId;

should result in a getter method with the following signature:

public Address getAddress() {
  return ReferenceResolver.resolve(this, addressId, Address.class);
}

The details what the resolver is doing is basically not relevant but the method signature for the resolve() method is the following:

public <T extends StoredObject> T resolve(StoredObject source, String key, Class<T> targetType)

Within the apply() method of the plugin, I am looking up the fields with their names and the corresponding annotation with the expected name and type of the property getter.

builder = builder.defineMethod( "get" + name, targetType, Visibility.PUBLIC )
         .intercept(MethodCall.invoke(delegate)
                          .withThis()
                      .withField(field.getName())
                      .with(targetType));

This does work, as long as targetType is StoredObject.class but as soon as targetType is set to the expected target type Address.class which inherits from StoredObject, the builder bails out with the following message:

Failed to transform class maku.User: [java.lang.IllegalStateException: Cannot return T from public maku.Address maku.User.getAddress()]

The resolver is performing the necessary checks to guarantee that the returned value is assignable to the expected target type and returns the values casted to T. However, somehow the generated code is not able to resolve the returned T correctly as it's bounded type StoredObject is a superclass of the expected type and thus it is not clear if casting it would return the correct value.

I believe that I need to perform one of the following things to make it work:

  • I either need to loosen the restrictions on the returned type within ByteBuddy,
  • or that I need to cast the returned value myself to the expected type,
  • I need to bind the generic type variable T to the expected type.

I have not found a way AKA some example showing how to accomplish this. Did I oversee anything or is it just not possible?


Solution

  • This is a limitation of Byte Buddy that it cannot infer method variables. Therefore, it reduces the return type to its erasure. You can however request Byte Buddy to cast the return value, which javac would anyways.

    Simply add .with(Assigner.DEFAULT, Assigner.Typing.DYNAMIC) and the cast will work.