बुरा शिष्टाचार।
जैसा कि गिक्स ने इंगित किया है, जिस क्षण आप जेनेरिक प्रकारों के पदानुक्रम को पहले से आगे बढ़ना शुरू करते हैं, आप टाइप तर्कों के बारे में जानकारी खो देते हैं।
लेकिन महत्वपूर्ण बिट्स हैं: आपको इंस्टेंस किए जाने वाले पहले जेनेरिक इंटरफ़ेस के प्रकार के तर्क मिलते हैं (मेरे उदाहरण में, विस्तारित इंटरफेस), और आपको उप-इंटरफेस बनाने के लिए उपयोग किए गए प्रकार पैरामीटर के नाम भी मिलते हैं।
तो, प्रकार टाइप करने योग्य नामों के मानचित्र को वास्तविक प्रकार के तर्कों के आधार पर आधार इंटरफेस के प्रकार के तर्कों को निर्धारित करना संभव है।
मैं बाद में कुछ कोड के साथ अपडेट करूंगा, लेकिन यह काम करता है (आप MyClass.class से बेसइंटरफेस उदाहरण के लिए प्रयुक्त प्रकार पैरामीटर निर्धारित कर सकते हैं)।
अद्यतन यह पहला पास है कि हरित रोशनी कुछ सरल इकाई परीक्षण करती है। इसे काम की ज़रूरत है ... असली सवाल यह है कि क्या समस्या इस तरह के एक लुभावनी समाधान की योग्यता है?
public class GenericReflectionUtils
{
@SuppressWarnings("unchecked")
public static List<Class> getGenericInterfaceTypeArguments(Class baseInterface, Class concreteClass)
{
if (!baseInterface.isAssignableFrom(concreteClass))
{
throw new IllegalArgumentException("Illegal base interface argument");
}
if (concreteClass.getTypeParameters().length > 0)
{
throw new IllegalArgumentException("Can't determine the type arguments of a generic interface of a generic class");
}
for (Type genericInterface : concreteClass.getGenericInterfaces())
{
List<Class> result = null;
if (genericInterface instanceof Class)
{
result = getGenericInterfaceTypeArguments(baseInterface,(Class)genericInterface);
}
else
{
result = getGenericInterfaceTypeArguments(baseInterface, (ParameterizedType)genericInterface);
}
if (result != null)
{
return result;
}
}
return null;
}
public static Class getClass(Type type)
{
if (type instanceof Class)
{
return (Class) type;
}
if (type instanceof ParameterizedType)
{
return getClass(((ParameterizedType) type).getRawType());
}
if (type instanceof GenericArrayType)
{
Type componentType = ((GenericArrayType) type).getGenericComponentType();
Class<?> componentClass = getClass(componentType);
if (componentClass != null)
{
return Array.newInstance(componentClass, 0).getClass();
}
return null;
}
return null;
}
@SuppressWarnings("unchecked")
private static List<Class> getGenericInterfaceTypeArguments(Class baseInterface, ParameterizedType currentType)
{
Class currentClass = getClass(currentType);
if (!baseInterface.isAssignableFrom(currentClass))
{
// Early out - current type is not an interface that extends baseInterface
return null;
}
Type[] actualTypeArguments = currentType.getActualTypeArguments();
if (currentClass == baseInterface)
{
// currentType is a type instance of the base generic interface. Read out the type arguments and return
ArrayList<Class> typeArgs = new ArrayList<Class>(actualTypeArguments.length);
for (Type typeArg : actualTypeArguments)
{
typeArgs.add(getClass(typeArg));
}
return typeArgs;
}
// currentType is derived
Map<String, Class> typeVarMap = createTypeParameterMap(currentType, null);
for (Type genericInterfaceType : currentClass.getGenericInterfaces())
{
List<Class> result = getGenericInterfaceTypeArguments(baseInterface, (ParameterizedType)genericInterfaceType, typeVarMap);
if (result != null)
{
return result;
}
}
return null;
}
private static Map<String, Class> createTypeParameterMap(ParameterizedType type, Map<String, Class> extendedTypeMap)
{
Map<String, Class> typeVarMap = new HashMap<String, Class>();
Type[] typeArgs = type.getActualTypeArguments();
TypeVariable[] typeVars = getClass(type).getTypeParameters();
for (int typeArgIndex = 0; typeArgIndex < typeArgs.length; ++typeArgIndex)
{
// Does not deal with nested generic arguments...
Type typeArg = typeArgs[typeArgIndex];
if (typeArg instanceof TypeVariable)
{
assert extendedTypeMap != null;
TypeVariable typeVar = (TypeVariable)typeArg;
typeVarMap.put(typeVars[typeArgIndex].getName(), extendedTypeMap.get(typeVar.getName()));
continue;
}
typeVarMap.put(typeVars[typeArgIndex].getName(), getClass(typeArgs[typeArgIndex]));
}
return typeVarMap;
}
private static List<Class> createTypeParameterList(Map<String, Class> typeParameterMap, ParameterizedType type)
{
ArrayList<Class> typeParameters = new ArrayList<Class>(typeParameterMap.size());
for (Type actualType : type.getActualTypeArguments())
{
if (actualType instanceof TypeVariable)
{
// Handles the case when an interface is created with a specific type, rather than a parameter
typeParameters.add(typeParameterMap.get(((TypeVariable)actualType).getName()));
continue;
}
typeParameters.add(getClass(actualType));
}
return typeParameters;
}
@SuppressWarnings("unchecked")
private static List<Class> getGenericInterfaceTypeArguments(Class baseInterface, ParameterizedType currentType, Map<String, Class> currentTypeParameters)
{
Class currentClass = getClass(currentType);
if (!baseInterface.isAssignableFrom(currentClass))
{
// Early out - current type is not an interface that extends baseInterface
return null;
}
if (currentClass == baseInterface)
{
return createTypeParameterList(currentTypeParameters, currentType);
}
currentTypeParameters = createTypeParameterMap(currentType, currentTypeParameters);
for (Type genericInterface : currentClass.getGenericInterfaces())
{
List<Class> result = getGenericInterfaceTypeArguments(baseInterface, (ParameterizedType)genericInterface, currentTypeParameters);
if (result != null)
{
return result;
}
}
return null;
}
}
टाइप पैरामीटर पैरामीटर प्रकार के उदाहरण से पुनर्प्राप्त किया जा सकता है। आपके उदाहरण में, A.getClass() को ArrayList को पारित प्रकार स्ट्रिंग को पुनर्प्राप्त करने के लिए खनन किया जा सकता है। – Andy
गलती, नहीं, यह –
हां नहीं, क्षमा करें, मेरी टिप्पणी बॉबिन (देर रात मस्तिष्क फार्ट) थी। मेरा मतलब यह था कि, यदि आपके पास ArrayList के आस-पास एक गैर-जेनेरिक रैपर वर्ग है, तो आप इसे कर सकते हैं। सही? ओह। – Andy