2011-02-02 12 views
8

निम्नलिखित कोड कंकाल को देखते हुए, यह निर्धारित करना संभव है कि foo संपत्ति String प्रकार की है? टी वर्ग java.lang.Objectजेनिक्स और java.beans.Introspector

संपादित करें:: मैं mentionend होना चाहिए

public class TestIntrospection { 
    public static class SuperBean<T> { 
     private T foo; 

     public T getFoo() { return foo; } 
     public void setFoo(T foo) { this.foo = foo; } 
    } 

    public static class SubBean extends SuperBean<String> { 
    } 

    public static void main(String[] args) throws IntrospectionException { 
     BeanInfo beanInfo = Introspector.getBeanInfo(SubBean.class); 
     PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); 
     for (PropertyDescriptor prop : propertyDescriptors) { 
      if ("foo".equals(prop.getName())) { 
       System.out.printf("%s of %s\n", prop.getName(), prop.getPropertyType()); 

       Method readMethod = prop.getReadMethod(); 
       Type returnType = prop.getReadMethod().getGenericReturnType(); 
       if (returnType instanceof TypeVariable) { 
        TypeVariable t = (TypeVariable) returnType; 
        GenericDeclaration d = t.getGenericDeclaration(); 
        System.out.println("TypeVariable : " + t.getName() + " " + t.getBounds()[0]); 
       } 
      } 
     } 
    } 
} 

वास्तविक उत्पादन वर्ग के

foo java.lang.Object
TypeVariable है कि मैं टाइप एरर के बारे में जानता हूं और यह कि वास्तव में विधि बाइटकोड स्तर पर ऑब्जेक्ट लौट रही है। फिर भी, जेनेरिक प्रकारों के बारे में मेटाडेटा कक्षा फ़ाइल में उपलब्ध है और नमूना कोड के रूप में प्रतिबिंब द्वारा पूछताछ की जा सकती है।

   Type superClass = SubBean.class.getGenericSuperclass(); 
       ParameterizedType pt = (ParameterizedType) superClass; 
       System.out.println(pt.getActualTypeArguments()[0]); 

उत्पादन: यहाँ एक और टुकड़ा पता चलता है कि वास्तव में SubBean प्रकार स्ट्रिंग का एक प्रकार पैरामीटर है कि

वर्ग java.lang.String

सवाल तो बना हुआ है, मैं इस वास्तविक प्रकार तर्क को प्रकार परिवर्तनीय से कैसे जोड़ूं? अगर मुझे पता है कि केवल एक प्रकार का पैरामीटर है तो यह आसान है, लेकिन मैं यह कोड चाहता हूं कि कई सामान्य प्रकार के पैरामीटर वाले बीन्स के लिए भी यह कोड काम करे।

+1

सहपाठी जावा http://www.cowtowncoder.com/blog/archives/2012/04/entry_471.html में वर्णित पुस्तकालय अच्छी तरह से सामान्य प्रकार के हल करने का समर्थन करने लगता है। यह परियोजना github पर उपलब्ध है: https://github.com/cowtowncoder/java-classmate –

+0

जावा 1.7.0_80 के रूप में आउटपुट वर्ग java.lang.String टाइप करने योग्य है: टी क्लास java.lang.Object' –

उत्तर

5

जब तक वस्तु के क्रम वर्ग प्रकार पैरामीटर के मूल्य को निर्धारित के रूप में

class Substitution extends HashMap<String, TypeExpr> { 
    Substitution(TypeVariable[] formals, TypeExpr[] actuals) { 
     for (int i = 0; i < actuals.length; i++) { 
      put(formals[i].getName(),actuals[i]); 
     } 
    } 

} 

abstract class TypeExpr { 
    abstract TypeExpr apply(Substitution s); 

    public abstract String toString(); 

    static TypeExpr from(Type type) { 
     if (type instanceof TypeVariable) { 
      return new TypeVar((TypeVariable) type); 
     } else if (type instanceof Class) { 
      return new ClassType((Class) type); 
     } else if (type instanceof ParameterizedType) { 
      return new ClassType((ParameterizedType) type); 
     } else if (type instanceof GenericArrayType) { 
      return new ArrayType((GenericArrayType) type); 
     } else if (type instanceof WildcardType) { 
      return new WildcardTypeExpr((WildcardType) type); 
     } 
     throw new IllegalArgumentException(type.toString()); 
    } 

    static TypeExpr[] from(Type[] types) { 
     TypeExpr[] t = new TypeExpr[types.length]; 
     for (int i = 0; i < types.length; i++) { 
      t[i] = from(types[i]); 
     } 
     return t; 
    } 

    static TypeExpr[] apply(TypeExpr[] types, Substitution s) { 
     TypeExpr[] t = new TypeExpr[types.length]; 
     for (int i = 0; i < types.length; i++) { 
      t[i] = types[i].apply(s); 
     } 
     return t; 
    } 

    static void append(StringBuilder sb, String sep, Object[] os) { 
     String s = ""; 
     for (Object o : os) { 
      sb.append(s); 
      s = sep; 
      sb.append(o); 
     } 
    } 
} 

class TypeVar extends TypeExpr { 
    final String name; 

    public TypeVar(String name) { 
     this.name = name; 
    } 

    public TypeVar(TypeVariable var) { 
     name = var.getName(); 
    } 

    @Override 
    public String toString() { 
     return name; 
    } 

    @Override 
    TypeExpr apply(Substitution s) { 
     TypeExpr e = s.get(name); 
     return e == null ? this : e; 
    } 
} 

class ClassType extends TypeExpr { 
    final Class clazz; 
    final TypeExpr[] arguments; // empty if the class is not generic 

    public ClassType(Class clazz, TypeExpr[] arguments) { 
     this.clazz = clazz; 
     this.arguments = arguments; 
    } 

    public ClassType(Class clazz) { 
     this.clazz = clazz; 
     arguments = from(clazz.getTypeParameters()); 
    } 

    @Override 
    public String toString() { 
     String name = clazz.getSimpleName(); 
     if (arguments.length == 0) { 
      return name; 
     } 

     StringBuilder sb = new StringBuilder(); 
     sb.append(name); 
     sb.append("<"); 
     append(sb, ", ", arguments); 
     sb.append(">"); 
     return sb.toString(); 
    } 

    public ClassType(ParameterizedType pt) { 
     clazz = (Class) pt.getRawType(); 
     Type[] args = pt.getActualTypeArguments(); 
     arguments = TypeExpr.from(args); 
    } 

    @Override 
    ClassType apply(Substitution s) { 
     return new ClassType(clazz, apply(arguments, s)); 
    } 
} 

class ArrayType extends TypeExpr { 
    final TypeExpr componentType; 

    public ArrayType(TypeExpr componentType) { 
     this.componentType = componentType; 
    } 

    public ArrayType(GenericArrayType gat) { 
     this.componentType = TypeExpr.from(gat.getGenericComponentType()); 
    } 

    @Override 
    public String toString() { 
     return componentType + "[]"; 
    } 

    @Override 
    TypeExpr apply(Substitution s) { 
     return new ArrayType(componentType.apply(s)); 
    } 
} 

class WildcardTypeExpr extends TypeExpr { 
    final TypeExpr[] lowerBounds; 
    final TypeExpr[] upperBounds; 

    public WildcardTypeExpr(TypeExpr[] lowerBounds, TypeExpr[] upperBounds) { 
     this.lowerBounds = lowerBounds; 
     this.upperBounds = upperBounds; 
    } 

    WildcardTypeExpr(WildcardType wct) { 
     lowerBounds = from(wct.getLowerBounds()); 
     upperBounds = from(wct.getUpperBounds()); 
    } 

    @Override 
    TypeExpr apply(Substitution s) { 
     return new WildcardTypeExpr(
      apply(lowerBounds, s), 
      apply(upperBounds, s) 
     ); 
    } 

    @Override 
    public String toString() { 
     StringBuilder sb = new StringBuilder(); 
     sb.append("?"); 
     if (lowerBounds.length > 0) { 
      sb.append(" super "); 
      append(sb, " & ", lowerBounds); 
     } 
     if (upperBounds.length > 0) { 
      sb.append(" extends "); 
      append(sb, " & ", upperBounds); 
     } 
     return sb.toString(); 
    } 
} 

public class Test { 

    /** 
    * @return {@code superClazz}, with the replaced type parameters it has for 
    *   instances of {@code ct}, or {@code null}, if {@code superClazz} 
    *   is not a super class or interface of {@code ct} 
    */ 
    static ClassType getSuperClassType(ClassType ct, Class superClazz) { 
     if (ct.clazz == superClazz) { 
      return ct; 
     } 

     Substitution sub = new Substitution(ct.clazz.getTypeParameters(), ct.arguments); 

     Type gsc = ct.clazz.getGenericSuperclass(); 
     if (gsc != null) { 
      ClassType sct = (ClassType) TypeExpr.from(gsc); 
      sct = sct.apply(sub); 
      ClassType result = getSuperClassType(sct, superClazz); 
      if (result != null) { 
       return result; 
      } 
     } 

     for (Type gi : ct.clazz.getGenericInterfaces()) { 
      ClassType st = (ClassType) TypeExpr.from(gi); 
      st = st.apply(sub); 
      ClassType result = getSuperClassType(st, superClazz); 
      if (result != null) { 
       return result; 
      } 

     } 
     return null; 
    } 

    public static ClassType getSuperClassType(Class clazz, Class superClazz) { 
     return getSuperClassType((ClassType) TypeExpr.from(clazz), superClazz); 
    } 

टेस्ट कोड:

, आप पुनरावर्ती) Class.getGenericSuperClass (से प्राप्त वास्तविक लोगों द्वारा औपचारिक प्रकार पैरामीटर की जगह अपने वास्तविक मूल्य का अनुमान लगा सकते
public static void check(Class c, Class sc, String expected) { 
     String actual = getSuperClassType(c, sc).toString(); 
     if (!actual.equals(expected)) { 
      throw new AssertionError(actual + " != " + expected); 
     } 
    } 

    public static void main(String[] args) { 
     check(Substitution.class, Map.class, "Map<String, TypeExpr>"); 
     check(HashMap.class, Map.class, "Map<K, V>"); 
     check(Bar.class, Foo.class, "Foo<List<? extends String[]>>"); 
    } 
} 

interface Foo<X> { 

} 
class SuperBar<X, Y> implements Foo<List<? extends Y[]>> { 

} 

class Bar<X> extends SuperBar<X, String> { } 

यदि दूसरी तरफ वर्ग प्रकार पैरामीटर के मान को निर्धारित नहीं करता है, तो आपको अन्य प्रकार से रनटाइम पर वास्तविक प्रकार पैरामीटर के लिए क्लास ऑब्जेक्ट को बनाए रखने के लिए अपनी बीन का विस्तार करना होगा, उदा। ऐसा करके:

class Super<T> { 
    final Class<T> clazz; 

    T foo; 

    Super(Class<T> clazz) { 
     this.clazz = clazz; 
    } 

    public T getFoo() { 
     return foo; 
    } 

    public T setFoo() { 
     this.foo = foo; 
    } 
} 
1

संकलन के दौरान जावा जेनरिक अनुभव प्रकार मिटाएं। रनटाइम पर, संकलन के दौरान मौजूद टी के प्रकार को निर्धारित करना संभव नहीं है। type erasure

+1

मुझे डर है कि उन मामलों में से एक है जहां जावा ट्यूटोरियल पूर्ण सत्य नहीं बताता है। उनका कहना है कि वे टाइप पैरामीटर बाइटकोड में मौजूद नहीं हैं। घोषित प्रकार, भले ही वे सामान्य हैं, कक्षा फ़ाइल में मौजूद हैं और प्रतिबिंब का उपयोग करके निरीक्षण किया जा सकता है। प्रश्न से कोड नमूने में, वह जानकारी रनटाइम पर प्रकार पैरामीटर को पुनर्निर्माण के लिए पर्याप्त है। – meriton

0

दुर्भाग्य से, नहीं:

जेनेरिक्स प्रकार विलोपन द्वारा कार्यान्वित किया जाता है: सामान्य प्रकार की जानकारी केवल संकलन समय पर मौजूद है, जिसके बाद यह संकलक द्वारा मिटा दिया जाता है

यहाँ एक कड़ी है। इस दृष्टिकोण का मुख्य लाभ यह है कि यह सामान्य कोड और विरासत कोड के बीच कुल अंतःक्रियाशीलता प्रदान करता है जो गैर-पैरामीटरयुक्त प्रकारों (जिन्हें तकनीकी रूप से कच्चे प्रकार के रूप में जाना जाता है) का उपयोग करता है। मुख्य नुकसान यह है कि पैरामीटर प्रकार की जानकारी रन टाइम पर उपलब्ध नहीं है, और दुर्भावनापूर्ण विरासत कोड के साथ अंतःक्रिया करते समय स्वचालित रूप से जेनरेट किए गए कास्ट विफल हो सकते हैं। हालांकि, दुर्भावनापूर्ण विरासत कोड के साथ अंतःक्रिया करते समय भी सामान्य संग्रह के लिए गारंटीकृत रन-टाइम प्रकार की सुरक्षा प्राप्त करने का एक तरीका है।

से http://download.oracle.com/javase/1.5.0/docs/guide/language/generics.html

+2

मुझे डर है कि उन मामलों में से एक है जहां जावा ट्यूटोरियल पूर्ण सत्य नहीं बताता है। उनका कहना है कि वे टाइप पैरामीटर बाइटकोड में मौजूद नहीं हैं। घोषित प्रकार, भले ही वे सामान्य हैं, कक्षा फ़ाइल में मौजूद हैं और प्रतिबिंब का उपयोग करके निरीक्षण किया जा सकता है। प्रश्न से कोड नमूने में, वह जानकारी रनटाइम पर प्रकार पैरामीटर को पुनर्निर्माण के लिए पर्याप्त है। – meriton

2

उद्धृत आप this hack के माध्यम से सामान्य के रनटाइम प्रकार प्राप्त कर सकते हैं। लिंक से निकाला गया कोड।

public class Base<T> { 

     private final Class<T> klazz; 

     @SuppressWarnings("unchecked") 
     public Base() { 
     Class<? extends Base> actualClassOfSubclass = this.getClass(); 
     ParameterizedType parameterizedType = (ParameterizedType) actualClassOfSubclass.getGenericSuperclass(); 
     Type firstTypeParameter = parameterizedType.getActualTypeArguments()[0]; 
     this.klazz = (Class) firstTypeParameter; 
     } 

     public boolean accepts(Object obj) { 
     return this.klazz.isInstance(obj); 
     } 

    } 

    class ExtendsBase extends Base<String> { 

     // nothing else to do! 

    } 
public class ExtendsBaseTest { 

    @Test 
    public void testTypeDiscovery() { 
    ExtendsBase eb = new ExtendsBase(); 
    assertTrue(eb.accepts("Foo")); 
    assertFalse(eb.accepts(123)); 
    } 
} 
0

यहाँ SuperBean की बाइट कोड है:

public class foo.bar.SuperBean { 

    // Field descriptor #6 Ljava/lang/Object; 
    // Signature: TT; 
    private java.lang.Object foo; 

    // Method descriptor #10()V 
    // Stack: 1, Locals: 1 
    public SuperBean(); 
    0 aload_0 [this] 
    1 invokespecial java.lang.Object() [12] 
    4 return 
     Line numbers: 
     [pc: 0, line: 3] 
     Local variable table: 
     [pc: 0, pc: 5] local: this index: 0 type: foo.bar.SuperBean 
     Local variable type table: 
     [pc: 0, pc: 5] local: this index: 0 type: foo.bar.SuperBean<T> 

    // Method descriptor #21()Ljava/lang/Object; 
    // Signature:()TT; 
    // Stack: 1, Locals: 1 
    public java.lang.Object getFoo(); 
    0 aload_0 [this] 
    1 getfield foo.bar.SuperBean.foo : java.lang.Object [24] 
    4 areturn 
     Line numbers: 
     [pc: 0, line: 8] 
     Local variable table: 
     [pc: 0, pc: 5] local: this index: 0 type: foo.bar.SuperBean 
     Local variable type table: 
     [pc: 0, pc: 5] local: this index: 0 type: foo.bar.SuperBean<T> 

    // Method descriptor #27 (Ljava/lang/Object;)V 
    // Signature: (TT;)V 
    // Stack: 2, Locals: 2 
    public void setFoo(java.lang.Object foo); 
    0 aload_0 [this] 
    1 aload_1 [foo] 
    2 putfield foo.bar.SuperBean.foo : java.lang.Object [24] 
    5 return 
     Line numbers: 
     [pc: 0, line: 12] 
     [pc: 5, line: 13] 
     Local variable table: 
     [pc: 0, pc: 6] local: this index: 0 type: foo.bar.SuperBean 
     [pc: 0, pc: 6] local: foo index: 1 type: java.lang.Object 
     Local variable type table: 
     [pc: 0, pc: 6] local: this index: 0 type: foo.bar.SuperBean<T> 
     [pc: 0, pc: 6] local: foo index: 1 type: T 
} 

आप देख सकते हैं, दोनों गेटर और सेटर प्रकार java.lang.Object के हैं। Introspector प्रॉपर्टीडिस्क्रिप्टर (फ़ील्ड को अनदेखा करने के लिए गेटर्स और सेटर्स का उपयोग करता है), इसलिए कोई सामान्य तरीका नहीं है कि संपत्ति सामान्य प्रकार के टी को जान सके।

+0

जिस वस्तु का निरीक्षण किया जा रहा है वह 'सबबीन' प्रकार है, न कि 'सुपरबीन'। 'सबबीन' की कक्षा फ़ाइल में पूर्ण विस्तार खंड शामिल है, जो बताता है कि 'टी' वास्तव में 'सबबीन' के उदाहरणों के लिए स्ट्रिंग का खड़ा है। यह जानकारी रनटाइम पर पहुंच योग्य है। – meriton

+0

@meriton True (मुझे लगता है कि मैंने इस सवाल पर काफी अच्छा नज़र डाला नहीं है)। फिर भी, विधियों को सुपरक्लास में परिभाषित किया गया है और इंट्रोस्पेक्टर उन परिभाषाओं का उपयोग करता है और बच्चे की सामान्य प्रकार की जानकारी को अनदेखा करता है। –

1

दुर्भाग्यवश, टाइप मिटाएं पूर्ण बल में है।

हालांकि ऐसा लगता है कि सबबीन के पास उस ivar और उन विधियों के लिए एक निश्चित प्रकार का स्ट्रिंग होना चाहिए क्योंकि SuperBean के लिए प्रकार पैरामीटर संकलित समय पर ज्ञात है, दुर्भाग्य से, यह जिस तरह से काम करता है। वहाँ केवल एक ही है (टाइप मिट) SuperBean

एक संभवतः बदसूरत वैकल्पिक हल है कि हालांकि मेरे लिए होता है कि SubBean है - से संकलक SubBean के लिए संकलन समय पर SuperBean के String -ified संस्करण का निर्माण नहीं करता प्राप्त करने के लिए और विशेष प्रकार के संस्करण के साथ सुपर क्लास विधि ओवरराइड करने के लिए सक्षम हो सकता है तो BeanInfo वापस आ सकते हैं आप तरीकों के लिए क्या उम्मीद:

public static class SubBean 
extends SuperBean<String> { 
    // Unfortunate this is necessary for bean reflection ... 
    public String getFoo()   { return super.getFoo(); } 
    public void setFoo(String foo) { super.setFoo(foo); } 
} 

अद्यतन: उपर्युक्त काम नहीं करता है।

यह Introspector अभी भी प्रकार वस्तु का रीड विधि लौटा रहा है के रूप में काम करने के लिए प्रतीत नहीं होता: यह जानकारी है कि @ टिप्पणी में Jörn Horstmann पदों पर ध्यान दें। इसके अतिरिक्त, यह एक जेनरेट ब्रिज विधि (http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ102) प्रतीत होता है जिसका अर्थ है कि यदि मैं इस विधि पर एनोटेशन एक्सेस करना चाहता हूं तो मैं bugs.sun.com/view_bug.do?bug_id=6788525 में चला सकता हूं।

public static class SubBean 
extends SuperBean<String> { 
    // Unfortunate this is necessary for bean reflection ... 
    public String getFooItem()   { return super.getFoo(); } 
    public void setFooItem(String foo) { super.setFoo(foo); } 
} 

SubBean अब एक विशिष्ट संपत्ति FooItem मूल SuperBean संपत्ति Foo के लिए एक उपनाम है कि है:

ऊपर वैकल्पिक हल का एक और बदसूरत भिन्नता संपत्ति अन्य नाम पर है।

+1

ऐसा लगता है कि इंट्रोस्पेक्टर अभी भी ऑब्जेक्ट प्रकार की रीड विधि वापस नहीं कर रहा है। इसके अतिरिक्त, यह एक जेनरेट ब्रिज विधि (http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ102) प्रतीत होता है जिसका अर्थ है कि मैं http://bugs.sun.com/view_bug.do में चला सकता हूं अगर मैं इस विधि पर एनोटेशन एक्सेस करना चाहता हूं तो bug_id = 6788525। –

+0

@ जोर्न होर्स्टमान - अपडेट के लिए धन्यवाद। मुझे खेद है कि यह आपके लिए काम नहीं करता - ऐसा लगता है कि यह चाहिए, लेकिन संकलक उत्पन्न पुल विधियों पर जानकारी दी गई, मैं भ्रम देख सकता हूं। एकमात्र अन्य सुझाव जो मैं सोच सकता हूं वह उपनाम संपत्ति को लागू करना है, उदा। 'पब्लिक स्ट्रिंग getFooItem() {वापसी super.getFoo(); } 'सबबियन' को एक विशिष्ट संपत्ति 'FooItem' देता है जो वास्तव में 'Foo' के लिए उपनाम है। –

+1

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

3

मुझे ऐसे मामले का समाधान मिला जहां एक सुपर वर्ग (ऑब्जेक्ट के अलावा) के साथ पदानुक्रम है जो सुपर क्लास पर कई प्रकार के पैरामीटर होने पर भी काम करता है।

अभी भी गहन पदानुक्रमों के लिए या जेनेरिक इंटरफेस को लागू करते समय काम नहीं करेगा। इसके अलावा मुझे एक पुष्टिकरण भी चाहिए कि वास्तव में यह दस्तावेज किया गया है और काम करना चाहिए।

public static class SuperBean<F, B, Q> { 
    // getters and setters 
} 

public static class SubBean<X> extends SuperBean<String, Integer, X> { 
} 

...

   Type returnType = readMethod.getGenericReturnType(); 

       Type superClass = SubBean.class.getGenericSuperclass(); 
       GenericDeclaration genericDecl = ((TypeVariable) returnType).getGenericDeclaration(); 
       TypeVariable[] parameters = genericDecl.getTypeParameters(); 
       Type[]   actualArgs = ((ParameterizedType) superClass).getActualTypeArguments(); 

       for (int i=0; i<parameters.length; i++) { 
        //System.out.println(parameters[i] + " " + actualArgs[i]); 
        if (returnType == parameters[i]) { 
         System.out.println("Match : " + parameters[i] + " : " + actualArgs[i]); 
        } 
       } 

आउटपुट:

वर्ग java.lang.Object
ऑफ द मैच बार: बी: कक्षा java.lang.Integer
वर्ग के foo java.lang.Object
मैच: एफ: वर्ग java.lang.String
qux कक्षा java.lang का।वस्तु
मैच: प्रश्न: एक्स

मैं कुछ और परीक्षण लिखने के लिए क्या पिछले पिछले मामले :) के साथ क्या करना निर्धारण करने की आवश्यकता

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