2016-03-09 7 views
14

से कन्स्ट्रक्टर के साथ उप-वर्ग का उदाहरण कैसे बनाएं, मैं कक्षाओं के लिए एक रजिस्ट्री बनाना चाहता हूं जो सुपर क्लास के उप-वर्ग हैं। कक्षाएं मानचित्र में संग्रहीत होती हैं जो रजिस्ट्री के रूप में कार्य करती है। एक कुंजी के आधार पर रजिस्ट्री से एक वर्ग चुना जाता है और उस वर्ग का एक उदाहरण प्रतिबिंब के माध्यम से बनाया जाएगा।सुपर क्लास

मैं सुपर क्लास के कन्स्ट्रक्टर (1 पैरामीटर के साथ) के आधार पर कक्षा को तुरंत चालू करना चाहता हूं। यह केवल तभी काम करता है जब मैं सबक्लास में कन्स्ट्रक्टर घोषित करता हूं।

क्या एक सुपर क्लास के निर्माता का उपयोग करके कक्षा को तुरंत चालू करने का कोई तरीका है? क्या कोड कोड-सुरक्षित बनाने का कोई तरीका है?

उदाहरण कोड:

public class ReflectionTest { 

    /** 
    * Base class with no-args constructor and another constructor with 1 parameter 
    */ 
    public static class BaseClass { 

     Object object; 

     public BaseClass() { 
      System.out.println("Constructor with no args"); 
     } 

     public BaseClass(Object object) { 
      this.object = object; 
      System.out.println("Constructor with parameter= " + object); 
     } 

     public String toString() { 
      return "Object = " + object; 
     } 
    } 

    /** 
    * Subclass with 1 parameter constructor 
    */ 
    public static class SubClass1 extends BaseClass { 
     public SubClass1(Object object) { 
      super(object); 
     } 
    } 

    /** 
    * Subclass with no-args constructor 
    */ 
    public static class SubClass2 extends BaseClass { 

    } 

    public static void main(String[] args) { 

     // registry for classes 
     Map<Integer,Class<?>> registry = new HashMap<>(); 
     registry.put(0, SubClass1.class); 
     registry.put(1, SubClass2.class); 

     // iterate through classes and create instances 
     for(Integer key: registry.keySet()) { 

      // get class from registry 
      Class<?> clazz = registry.get(key); 

      try { 

       // get constructor with parameter 
       Constructor constructor = clazz.getDeclaredConstructor(Object.class); 

       // instantiate class 
       BaseClass instance = (BaseClass) constructor.newInstance(key); 

       // logging 
       System.out.println("Instance for key " + key + ", " + instance); 

      } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { 
       e.printStackTrace(); 
      } 

     } 

     System.exit(0); 
    } 
} 

उदाहरण निम्नलिखित कंसोल आउटपुट देता है:

Constructor with parameter= 0 
Instance for key 0, Object = 0 
java.lang.NoSuchMethodException: swing.table.ReflectionTest$SubClass2.<init>(java.lang.Object) 
    at java.lang.Class.getConstructor0(Class.java:3082) 
    at java.lang.Class.getConstructor(Class.java:1825) 
    at swing.table.ReflectionTest.main(ReflectionTest.java:63) 
+0

यदि आप किसी विशेष सबक्लास में शून्य और एक-तर्क निर्माता दोनों होते हैं तो आप क्या करना चाहते हैं? –

उत्तर

9
  1. एक सुपरक्लास को अपने बच्चों का कोई ज्ञान नहीं है, यह स्पष्ट कारणों के मामले में होना चाहिए।
  2. रचनाकार विरासत में नहीं हैं।

निष्कर्ष यह है कि सबक्लास सीटीओआर के बारे में धारणा किए बिना, आप जो कोड चाहते हैं उसे नहीं लिख सकते हैं।

तो आप क्या कर सकते हैं? सार फैक्टरी पैटर्न का उपयोग करें।

हम एक interface Factory बनाएँ:

@FunctionalInterface 
public interface SuperclassFactory { 
    Superclass newInstance(Object o); 
} 

आप Factory पर एक से अधिक विधि बना सकते हैं, लेकिन यह है कि lambdas के लिए यह कम साफ होगा।

अब आप एक Map<Integer, SuperclassFactory> है, और इसे पॉप्युलेट:

Map<Integer,SuperclassFactory> registry = new HashMap<>(); 
registry.put(0, SubClass1::new); 
registry.put(1, SubClass2::new); 

तो, क्रम में इस Map उपयोग करने के लिए आप बस कार्य करें: अपने उपवर्ग उचित ctor नहीं है

for(final Map.Entry<Integer,SuperclassFactory> e: registry.entrySet()) { 
    //... 
    final BaseClass instance = e.getValue().newInstance(e.getKey()); 
    //... 
} 

हैं, यह कोड संकलित नहीं होगा क्योंकि कोई सीटीओआर संदर्भ नहीं होगा जिसका उपयोग किया जा सकता है। यह अच्छी बात है (टीएम)। तो अब है

registry.put(1, obj -> new SubClass2()); 

:

  1. प्रतिबिंब
  2. हासिल कर ली संकलन समय प्रकार सुरक्षा
  3. बदसूरत डाली खो खो आदेश वर्तमान Subclass2 साथ संकलित करने के लिए आप का उपयोग करने की आवश्यकता होगी (हालांकि यह प्रतिबिंब के दुरुपयोग के माध्यम से था)

एनबी Map के entrySet() के माध्यम से लूप keySet() नहीं है।

+1

'e.getValue() लागू करें (e.getKey())' e.getValue() होना चाहिए। NewInstace (e.getKey()) '। – saka1029

+0

@ saka1029 yup - धन्यवाद। –

+0

महान उत्तर जो निश्चित रूप से समस्या को एक स्वच्छ और सुरक्षित तरीके से संभालता है (मेरी इच्छा है कि एसओ पर इनमें से अधिकतर हों)। मुझे [इस सवाल] की याद दिलाता है (http://stackoverflow.com/questions/35627246/instantiate-an-extended-class-or-its-parent-depending-on-circumstances)। – Spotted

1

कंस्ट्रक्टर्स उपवर्ग में स्पष्ट रूप से परिभाषित किया जाना चाहिए। यदि किसी कन्स्ट्रक्टर को सुपरक्लास में परिभाषित किया गया है जिसका अर्थ यह नहीं है कि कन्स्ट्रक्टर का उपयोग उप-वर्ग का उदाहरण बनाने के लिए किया जा सकता है चाहे आप प्रतिबिंब का उपयोग कर रहे हों या नहीं।

चूंकि आपके SubClass2 में एक तर्क के साथ कन्स्ट्रक्टर नहीं है, इसलिए जब आप इसे एक तर्क के साथ उदाहरण बनाने का प्रयास करते हैं, तो यह NoSuchMethodException फेंक रहा है।

1
  • कक्षा के सभी रचनाकारों को प्राप्त करने के लिए clazz.getDeclaredConstructors() का उपयोग करें;
  • सर्वोत्तम लागू कन्स्ट्रक्टर खोजने के लिए उनके माध्यम से इटरेट करें, उदा। यदि एकल-ऑब्जेक्ट-Arg कन्स्ट्रक्टर उपलब्ध नहीं है तो शून्य-तर्क चुनें;
  • उपयुक्त पैरामीटर का उपयोग कर उस कन्स्ट्रक्टर को आमंत्रित करें।

इस पर पूरी तरह से सुरक्षित होने का कोई तरीका नहीं है, क्योंकि आप पहले से नहीं जानते कि किसी भी लागू सार्वजनिक निर्माता किसी दिए गए वर्ग के लिए मौजूद हैं, उदाहरण के लिए सीटीआर निजी हो सकता है, या उपलब्ध कन्स्ट्रक्टर आपके इच्छित प्रकार के पैरामीटर स्वीकार नहीं कर सकता है (उदाहरण के लिए स्ट्रिंग की आवश्यकता है, लेकिन आपके पास केवल ऑब्जेक्ट है)।

+0

कोई तर्क दे सकता है कि प्रतिबिंब के जादू के साथ, 'निजी' बाधा नहीं है। लेकिन इस तरह पागलपन झूठ बोल रहा है ... –

0

आप निर्माता

clazz.getDeclaredConstructor(Object.class); 

प्राप्त करने के लिए इस लाइन का उपयोग कर रहे लेकिन अपने Subclass2 एक भी तर्क निर्माता इसलिए अपवाद फेंक दिया जाता है नहीं है।

उपयोग clazz.getDeclaredConstructors() विधि के बजाय और आह्वान निर्माता मापदंडों के आधार पर गिनती।

  BaseClass instance; 
      // get constructor with parameter 
      Constructor constructor = clazz.getDeclaredConstructors()[0]; 
      if (constructor.getParameterCount() == 1) { 
       instance = (BaseClass) constructor.newInstance(key); 
      } else { 
       instance = (BaseClass) constructor.newInstance(); 
      } 
+0

और यदि मेरे पास एक सीटीओआर है जो 'स्ट्रिंग' लेता है और वह 'नंबर' लेता है? आपको उन पैरामीटर के _types_ की जांच करने की आवश्यकता है। –

+0

यह उनके कोड में सही नहीं था? बेसक्लास उदाहरण = (बेस क्लास) constructor.newInstance (कुंजी); वह स्पष्ट रूप से पैरामीटर को पारित करने के साथ एक उदाहरण बनाना चाहता था। यदि वह एक स्ट्रिंग पास करना चाहता था तो वह कन्स्ट्रक्टर में कुंजी पारित नहीं करेगा। – mirmdasif

+0

निश्चित रूप से, यह सटीक उदाहरण नहीं है। लेकिन ओपी एक सामान्य समाधान की तलाश में है - मुझे यकीन है कि ओपी इस तरह की एक समस्या को किसी अन्य तरीके से हल करने में पूरी तरह से सक्षम है। सवाल यह पूछता है कि यह _in सामान्य_ कैसे करें - और यह इसके लिए एक पूर्ण उत्तर नहीं है। –

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