Skip to content

Instantly share code, notes, and snippets.

@Villane
Created March 15, 2010 18:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Villane/333185 to your computer and use it in GitHub Desktop.
Save Villane/333185 to your computer and use it in GitHub Desktop.
Licensed under Apache Software License 2.0
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;
/**
* Resolves generic types into concrete types where possible.
*
* Note: some snippets taken from Artima.com: http://www.artima.com/weblogs/viewpost.jsp?thread=208860
*
* @author erkki.lindpere
*/
public class GenericTypeResolver
{
/**
* @param field
* @param owner
* @return the most specific type possible for the field
*/
@SuppressWarnings("unchecked")
public static Class< ? > getFieldType(Field field, Object owner)
{
Type type = field.getGenericType();
if (type instanceof TypeVariable< ? >)
{
TypeVariable<GenericDeclaration> typeVar = (TypeVariable<GenericDeclaration>)type;
try
{
Type resolvedType = resolveTypeVar(typeVar, owner);
if (resolvedType instanceof Class< ? >)
{
return (Class< ? >)resolvedType;
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
return field.getType();
}
/**
* @param var
* @param owner
* @return the resolved type for the type variable
* @throws Exception
* @throws IllegalArgumentException
*/
@SuppressWarnings("unchecked")
public static Type resolveTypeVar(TypeVariable<GenericDeclaration> var, Object owner)
throws IllegalArgumentException, Exception
{
GenericDeclaration genDecl = var.getGenericDeclaration();
if (!(genDecl instanceof Class< ? >))
{
return null;
}
Class< ? > declClass = (Class< ? >)genDecl;
Class< ? > objectClass = owner.getClass();
assert (declClass.isAssignableFrom(objectClass));
Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
Type type = objectClass;
// start walking up the inheritance hierarchy until we hit baseClass
while (!getClass(type).equals(declClass.getSuperclass()))
{
if (type instanceof Class< ? >)
{
// there is no useful information for us in raw types, so just keep going.
type = ((Class< ? >)type).getGenericSuperclass();
}
else
{
ParameterizedType parameterizedType = (ParameterizedType)type;
Class< ? > rawType = (Class< ? >)parameterizedType.getRawType();
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
TypeVariable< ? >[] typeParameters = rawType.getTypeParameters();
for (int i = 0; i < actualTypeArguments.length; i++)
{
resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
}
type = rawType.getGenericSuperclass();
}
}
if (resolvedTypes.containsKey(var))
{
Type resolved = resolvedTypes.get(var);
if (resolved instanceof Class< ? >)
{
return resolved;
}
else if (resolved instanceof TypeVariable< ? >)
{
TypeVariable<GenericDeclaration> rsVar = (TypeVariable<GenericDeclaration>)resolved;
if (rsVar.getGenericDeclaration() == objectClass.getEnclosingClass())
{
Field enclosingInstanceField = objectClass.getDeclaredField("this$0");
if (!enclosingInstanceField.isAccessible())
{
enclosingInstanceField.setAccessible(true);
}
Object enclosingInstance = enclosingInstanceField.get(owner);
return resolveTypeVar(rsVar, enclosingInstance);
}
}
}
return null;
}
/**
* Get the underlying class for a type, or null if the type is a variable type.
*
* @param type
* the type
* @return the underlying class
*/
public static Class< ? > getClass(Type type)
{
if (type instanceof Class< ? >)
{
return (Class< ? >)type;
}
else if (type instanceof ParameterizedType)
{
return getClass(((ParameterizedType)type).getRawType());
}
else if (type instanceof GenericArrayType)
{
Type componentType = ((GenericArrayType)type).getGenericComponentType();
Class< ? > componentClass = getClass(componentType);
if (componentClass != null)
{
return Array.newInstance(componentClass, 0).getClass();
}
else
{
return null;
}
}
else
{
return null;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment