(The surface area of the ByteBuddy API is overwhelmingly enormous, which is why I'm asking the question.)
I'm aware that I can take a TypeDescription.Generic
and determine its "sort" and proceed rather laboriously "by hand" from there, but often times I've found there is a method buried somewhere that will do this sort of tedious work for me.
EDIT: a commenter asked for the "tedious" recipe. Here it is (stand back; please note the various implementations of various Type
s are more or less what you'd expect them to be):
public static final Type toType(final TypeDefinition type) throws ReflectiveOperationException {
final Type returnValue;
if (type == null) {
returnValue = null;
} else {
final TypeDescription.Generic genericType = type.asGenericType();
switch (type.getSort()) {
case GENERIC_ARRAY:
returnValue = new DefaultGenericArrayType(toType(type.getComponentType()));
break;
case NON_GENERIC:
returnValue = Class.forName(type.getTypeName(), false, Thread.currentThread().getContextClassLoader());
break;
case PARAMETERIZED:
final TypeDefinition ownerType = genericType.getOwnerType();
final TypeDefinition rawType = type.asErasure();
final List<? extends TypeDefinition> actualTypeArguments = genericType.getTypeArguments();
if (actualTypeArguments == null || actualTypeArguments.isEmpty()) {
returnValue = new DefaultParameterizedType(toType(ownerType), toType(rawType));
} else {
final Type[] actualJavaTypeArguments = new Type[actualTypeArguments.size()];
for (int i = 0; i < actualTypeArguments.size(); i++) {
actualJavaTypeArguments[i] = toType(actualTypeArguments.get(i));
}
returnValue = new DefaultParameterizedType(toType(ownerType), toType(rawType), actualJavaTypeArguments);
}
break;
case VARIABLE:
final TypeVariableSource typeVariableSource = genericType.getTypeVariableSource();
final GenericDeclaration gd;
if (typeVariableSource instanceof TypeDefinition typeDefinition) {
gd = Class.forName(typeDefinition.asErasure().getTypeName(), false, Thread.currentThread().getContextClassLoader());
} else if (typeVariableSource instanceof MethodDescription.InDefinedShape methodDescription) {
// Reflection time
final String name = methodDescription.getName();
final Class<?> cls = Class.forName(methodDescription.getDeclaringType().asErasure().getTypeName(), false, Thread.currentThread().getContextClassLoader());
final List<? extends TypeDefinition> parameterTypes = methodDescription.getParameters().asTypeList();
final Class<?>[] parameterClasses = new Class<?>[parameterTypes.size()];
for (int i = 0; i < parameterTypes.size(); i++) {
parameterClasses[i] = Class.forName(parameterTypes.get(i).asErasure().getName(), false, Thread.currentThread().getContextClassLoader());
}
if (MethodDescription.CONSTRUCTOR_INTERNAL_NAME.equals(name)) {
assert TypeDescription.VOID.equals(methodDescription.getReturnType());
gd = cls.getDeclaredConstructor(parameterClasses);
} else {
assert !MethodDescription.TYPE_INITIALIZER_INTERNAL_NAME.equals(name);
gd = cls.getDeclaredMethod(name, parameterClasses);
}
} else {
throw new IllegalArgumentException("Unexpected type variable source: " + typeVariableSource);
}
final TypeVariable<?>[] typeVariables = gd.getTypeParameters();
TypeVariable<?> temp = null;
for (final TypeVariable<?> typeVariable : typeVariables) {
if (typeVariable.getName().equals(genericType.getSymbol())) {
temp = typeVariable;
break;
}
}
assert temp != null;
returnValue = temp;
break;
case VARIABLE_SYMBOLIC:
throw new IllegalArgumentException("Unexpected type: " + type);
case WILDCARD:
final List<? extends TypeDefinition> upperBounds = genericType.getUpperBounds();
final List<? extends TypeDefinition> lowerBounds = genericType.getLowerBounds();
if (lowerBounds == null || lowerBounds.isEmpty()) {
if (upperBounds == null || upperBounds.isEmpty() || (upperBounds.size() == 1 && TypeDescription.Generic.OBJECT.equals(upperBounds.get(0)))) {
returnValue = UnboundedWildcardType.INSTANCE;
} else {
// Upper bounded.
final Type[] upperJavaBounds = new Type[upperBounds.size()];
for (int i = 0; i < upperBounds.size(); i++) {
upperJavaBounds[i] = toType(upperBounds.get(i)); // XXX recursive
}
returnValue = new UpperBoundedWildcardType(upperJavaBounds);
}
} else {
assert upperBounds == null || upperBounds.isEmpty() || (upperBounds.size() == 1 && TypeDescription.Generic.OBJECT.equals(upperBounds.get(0))) : "Unexpected upper bounds: " + upperBounds + "; lower bounds: " + lowerBounds;
// Lower bounded.
assert lowerBounds.size() == 1 : "Unexpected size in lower bounds: " + lowerBounds;
returnValue = new LowerBoundedWildcardType(toType(lowerBounds.get(0))); // XXX recursive
}
break;
default:
throw new IllegalArgumentException("Unexpected type: " + type);
}
}
return returnValue;
}
No, you can only convert a
Type
to aTypeDescription.Generic
but there is no option to do it the other way. The easiest option to emulate this would probably be to define a class that defines a field of the givenType
, to load this class and to read the field type using Java reflection.The reason Byte Buddy cannot convert a description to a
Type
is that Byte Buddy abstracts out class loaders and that type variables might be detached from their declaring source.