Created
March 15, 2010 18:55
-
-
Save Villane/333185 to your computer and use it in GitHub Desktop.
Licensed under Apache Software License 2.0
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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