2009-02-17 16 views
18

को देखते हुए 2 इंटरफेस:पुन: प्राप्त प्रकार पैरामीटर

public interface BaseInterface<T> { } 
public interface ExtendedInterface<T0, T1> extends BaseInterface<T0> {} 

और एक ठोस वर्ग:

public class MyClass implements ExtendedInterface<String, Object> { } 

मैं कैसे प्रकार पैरामीटर BaseInterface इंटरफेस के लिए पारित बाहर मिल रहा है ?

(मैं

MyClass.class.getGenericInterfaces()[0].getActualTypeArguments() 

की तरह कुछ फोन करके ExtendedInterface प्रकार पैरामीटर प्राप्त कर सकते हैं, लेकिन मैं किसी भी आधार जेनेरिक इंटरफेस में recurse और कुछ भी सार्थक वापस पाने के लिए एक आसान तरीका स्थान नहीं कर सकता)।

उत्तर

20

यह समस्या सामान्य रूप से पूरी तरह हल करने में आसान नहीं है। उदाहरण के लिए, यदि आप एक आंतरिक कक्षा है, तो आपको युक्त वर्ग के प्रकार पैरामीटर को भी ध्यान में रखना होगा ...

क्योंकि जेनेरिक प्रकारों पर प्रतिबिंब इतना ही कठिन है कि जावा स्वयं ही प्रदान करता है, मैंने एक लाइब्रेरी लिखी है कड़ी मेहनत: gentyref। http://code.google.com/p/gentyref/ अपने उदाहरण के लिए देखें, gentyref उपयोग कर, आप कर सकते हैं:

Type myType = MyClass.class; 

// get the parameterized type, recursively resolving type parameters 
Type baseType = GenericTypeReflector.getExactSuperType(myType, BaseInterface.class); 

if (baseType instanceof Class<?>) { 
    // raw class, type parameters not known 
    // ... 
} else { 
    ParameterizedType pBaseType = (ParameterizedType)baseType; 
    assert pBaseType.getRawType() == BaseInterface.class; // always true 
    Type typeParameterForBaseInterface = pBaseType.getActualTypeArguments()[0]; 
    System.out.println(typeParameterForBaseInterface); 
} 
0

मुझे नहीं लगता कि आप वास्तव में उदाहरण विशिष्ट विशिष्ट श्रेणी विशिष्ट नहीं हो सकते हैं। निम्नलिखित पर विचार करें:

List<String> a = new ArrayList<String>(); 

तथ्य यह है कि एक तार के सामान्य सूची है उदाहरण के एक करने के लिए विशिष्ट और वर्ग की सूची में नहीं है। इस प्रकार, List.class ऑब्जेक्ट के किसी भी तरीके से आपको यह नहीं बताया जा सकता है कि जेनेरिक प्रकार ए के लिए स्ट्रिंग प्रकार का होगा। यद्यपि MyClass आपके उदाहरण में इंटरफ़ेस के जेनेरिक प्रकारों के लिए मान निर्धारित करता है, मुझे नहीं लगता कि यह इंटरफ़ेस क्लास ऑब्जेक्ट इंस्टेंस पर उपलब्ध होगा।

+0

टाइप पैरामीटर पैरामीटर प्रकार के उदाहरण से पुनर्प्राप्त किया जा सकता है। आपके उदाहरण में, A.getClass() को ArrayList को पारित प्रकार स्ट्रिंग को पुनर्प्राप्त करने के लिए खनन किया जा सकता है। – Andy

+0

गलती, नहीं, यह –

+0

हां नहीं, क्षमा करें, मेरी टिप्पणी बॉबिन (देर रात मस्तिष्क फार्ट) थी। मेरा मतलब यह था कि, यदि आपके पास ArrayList के आस-पास एक गैर-जेनेरिक रैपर वर्ग है, तो आप इसे कर सकते हैं। सही? ओह। – Andy

0

मुझे लगता है कि एकमात्र विकल्प के बारे में मुझे लगता है कि BaseInterface द्वारा घोषित एक सामान्य विधि का निरीक्षण करना है, और ओवरराइड नहीं किया गया है।

6

मैं तुम्हें वास्तव में क्या हासिल करने की कोशिश कर रहे हैं, और क्या जाना जाता है और क्या नहीं पता नहीं है, लेकिन आप इस तरह superinterface को recurse कर सकते हैं: अगर आप दूसरे स्तर तक पहुँचने के

Type[] interfaces = MyClass.class.getGenericInterfaces(); 

ParameterizedType extInterfaceType = (ParameterizedType)interfaces[0]; 
Class<?> extInterfaceClass = (Class<?>)extInterfaceType.getRawType(); 

Type[] baseInterfaces = extInterfaceClass.getGenericInterfaces(); 
ParameterizedType baseInterfaceType = (ParameterizedType)baseInterfaces[0]; 
Class<?> baseInterfaceClass = (Class<?>)baseInterfaceType.getRawType(); 
बेशक

इस तरह आप जेनेरिक पैरामीटर के रूप में केवल अपने नाम टी 0 और टी 1 प्राप्त करते हैं। यदि आप ExtendedInterface और BaseInterface के बीच संबंधों को जानते हैं तो आपको वास्तव में उस समय तक जाना नहीं है क्योंकि आप जानते हैं कि पूर्व का कौन सा सामान्य पैरामीटर उत्तरार्द्ध में पारित किया गया है। यदि नहीं, तो आपको शायद अपने पैरामीटर के माध्यम से लूप करना होगा और एक मैच ढूंढना होगा। कुछ इस शायद के आधार पर:

Type[] params = extInterfaceClass.getTypeParameters(); 
for (Type param : params) { 
    if (param == baseInterfaceType.getActualTypeArguments()[0]) { 
     // ... 
    } 
} 
1

मैं वहाँ एक प्रत्यक्ष आधार अंतरफलक के सामान्य प्रकार होने का रास्ता नहीं लगता।

एक तरह से इस तरह इंटरफ़ेस में एक प्रणाली की घोषणा करने के लिए होगा:

public interface BaseInterface<T> { 
    Class<T> getGenericClass(); 
} 

इसके अलावा, मैं नियंत्रण किस तरह आप इन कक्षाओं से अधिक है पता नहीं है। तुम हमेशा जोर कर सकते हैं सब इसको लागू करने का आधार इंटरफ़ेस स्पष्ट रूप से की तरह घोषित है: से मुझे मेरे हाल सवाल का जवाब देने को फिर से

public class MyClass implements ExtendedInterface<String, Object>, BaseInterface<String>{ } 

और

MyClass.class.getGenericInterfaces()[1].getActualTypeArguments()[0] 
0

बुरा शिष्टाचार।

जैसा कि गिक्स ने इंगित किया है, जिस क्षण आप जेनेरिक प्रकारों के पदानुक्रम को पहले से आगे बढ़ना शुरू करते हैं, आप टाइप तर्कों के बारे में जानकारी खो देते हैं।

लेकिन महत्वपूर्ण बिट्स हैं: आपको इंस्टेंस किए जाने वाले पहले जेनेरिक इंटरफ़ेस के प्रकार के तर्क मिलते हैं (मेरे उदाहरण में, विस्तारित इंटरफेस), और आपको उप-इंटरफेस बनाने के लिए उपयोग किए गए प्रकार पैरामीटर के नाम भी मिलते हैं।

तो, प्रकार टाइप करने योग्य नामों के मानचित्र को वास्तविक प्रकार के तर्कों के आधार पर आधार इंटरफेस के प्रकार के तर्कों को निर्धारित करना संभव है।

मैं बाद में कुछ कोड के साथ अपडेट करूंगा, लेकिन यह काम करता है (आप 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; 
} 

}

+0

यह वास्तव में एक अच्छी शुरुआत की तरह दिखता है, जैसा कि gentyref करता है। इसे अभी भी कुछ काम पूरा होने की आवश्यकता है।नेस्टेड क्लासेस, नेस्टेड जेनरिक के बारे में सोचें, और फिर आपको नोटिस होगा कि आपको इंटरफ़ेस I जैसे मामलों में वाइल्डकार्ड को संभालने की आवश्यकता है बेसइंटरफेस <सूची > लागू करता है, ... –

+0

बहुत अच्छे अंक, धन्यवाद वाउटर। मैं इसे बाद में किसी और पूर्ण समाधान के साथ अपडेट कर दूंगा। इस पल के लिए, यह एक परियोजना में मेरे उद्देश्यों को एक तंग समय सीमा के साथ उपयुक्त बनाता है, इसलिए मैं इसे अभी छोड़ दूंगा। – Andy

1

यह थोड़े क्या करता है आप के बाद कर रहे हैं, लेकिन यह अभी भी ठीक नहीं है। उदाहरण के लिए, यह उस मामले को संभाल नहीं करता है जहां Foo<T> implements Bar<Map<T>> है। आपको वास्तव में जेवीएम से पूछने का कोई तरीका है "ठीक है, यहां प्रकारों की एक सूची है। अगर मैं इन सामान्य प्रकारों पर इन्हें लागू करता हूं तो मैं किस वास्तविक प्रकार को वापस प्राप्त करूं?"

लेकिन, यह कोड थोडा करता है जो आप के बाद करते हैं।

import java.lang.reflect.GenericDeclaration; 
import java.lang.reflect.ParameterizedType; 
import java.util.*; 

interface BaseInterface<T> {} 
interface FirstArg<T1,T2> extends BaseInterface<T1>{} 
interface SecondArg<T1,T2> extends BaseInterface<T2>{} 

class First implements FirstArg<Number, String> {} 
class Second implements SecondArg<Number, String> {} 


public class Example { 
    public static void main(String[] av) { 
     new Example().go(); 
    } 

    void go() { 
     test(First.class); 
     test(Second.class); 
    } 

    void test(Class<?> c1) {   
     ParameterizedType t2 = (ParameterizedType) c1.getGenericInterfaces()[0]; 
     System.out.println(c1 + " implements " + t2); 

     Class<?> c2 = (Class<?>)t2.getRawType(); 
     GenericDeclaration g2 = (GenericDeclaration) c2; 

     System.out.println(t2 + " params are " + Arrays.asList(g2.getTypeParameters())); 

     System.out.println("So that means"); 
     for(int i = 0; i<t2.getActualTypeArguments().length; i++) { 
      System.out.println("Parameter " + c2.getTypeParameters()[i] + " is " + t2.getActualTypeArguments()[i]); 
     } 

     ParameterizedType t3 = (ParameterizedType) c2.getGenericInterfaces()[0]; 
     System.out.println(t2 + " implements " + t3); 

     System.out.println("and so that means we are talking about\n" + t3.getRawType().toString() + " <"); 
     for(int i = 0 ; i< t3.getActualTypeArguments().length; i++) { 
      System.out.println("\t" + t3.getActualTypeArguments()[i] + " -> " 
      + Arrays.asList(g2.getTypeParameters()).indexOf(t3.getActualTypeArguments()[i]) 
      + " -> " + 
      t2.getActualTypeArguments()[Arrays.asList(g2.getTypeParameters()).indexOf(t3.getActualTypeArguments()[i])] 
      ); 
     } 

     System.out.println(">"); 
     System.out.println(); 
    } 

} 
+0

यह ऐसा कुछ है जैसे मैंने इसे भी कार्यान्वित किया। मैंने प्रकार परिवर्तनीय नामों का उपयोग किया, हालांकि, तर्क आदेश की गारंटी नहीं दी जा सकती है (मुझे लगता है) (मेरे उदाहरण में, विस्तारित इंटरफेस बेस इंटरफेस में टी 1 पास कर सकता है)। – Andy

+0

ठीक है, यह वही है जो मेरा कोड संभालता है - आप दो इंटरफेस नोट करेंगे, जो एक परम 1 पास करता है, और एक जो परम 2 पास करता है। – paulmurray

0

अपाचे कॉमन्स सिर्फ ऐसा करने के लिए एक उपयोगिता है ... http://commons.apache.org/lang/api/org/apache/commons/lang3/reflect/TypeUtils.html

देखें कि यह कैसे उनके GitHub पृष्ठ पर प्रयोग किया जाता है: https://github.com/apache/commons-lang/blob/72be39f4facb4a5758b9f646309328b764216da3/src/test/java/org/apache/commons/lang3/reflect/TypeUtilsTest.java

2

जावा प्रतिबिंब API का उपयोग करके हल करना मुश्किल है क्योंकि किसी को सभी प्रकार के प्रकार चर को हल करने की आवश्यकता है। संस्करण 12 के बाद से गुवा TypeToken कक्षा है जिसमें पूरी तरह से हल की गई प्रकार की जानकारी शामिल है।

अपने उदाहरण के लिए, आप कर सकते हैं:

TypeToken<? extends T> token = TypeToken.of(MyClass.class); 
ParameterizedType type = 
    (ParameterizedType) token.getSupertype(BaseInterface.class).getType(); 
Type[] parameters = type.getActualTypeArguments(); 

फिर भी आप को याद है कि मामलों के लिए यह केवल काम करता है जब MyClass ही साधारण नहीं है की जरूरत है। अन्यथा टाइप पैरामीटर का मान रनटाइम पर टाइप एरर के कारण उपलब्ध नहीं है।

संबंधित मुद्दे