जावा में जेनेरिक ऐरे कैसे बनाएं?


1090

जावा जेनेरिक के कार्यान्वयन के कारण, आपके पास इस तरह का कोड नहीं हो सकता है:

public class GenSet<E> {
    private E a[];

    public GenSet() {
        a = new E[INITIAL_ARRAY_LENGTH]; // error: generic array creation
    }
}

प्रकार सुरक्षा को बनाए रखते हुए मैं इसे कैसे लागू कर सकता हूं?

मैंने जावा फ़ोरम पर एक समाधान देखा जो इस प्रकार है:

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

लेकिन मुझे वास्तव में नहीं मिलता कि क्या चल रहा है।


14
क्या तुम सच में है की जरूरत है यहाँ एक सरणी उपयोग कैसे करें? एक संग्रह का उपयोग करने के बारे में क्या?
मैट बी

12
हां मुझे यह भी लगता है कि इस समस्या के लिए संग्रह अधिक सुरुचिपूर्ण हैं। लेकिन यह एक वर्ग के काम के लिए है और उन्हें आवश्यक है :(
tatsuhirosatou

3
मुझे समझ नहीं आ रहा है कि मुझे यहां परिलक्षित होने की आवश्यकता क्यों है। जावा व्याकरण अजीब है: नए java.util की तरह। हाशम <स्ट्रिंग, स्ट्रिंग> [10] मान्य नहीं है। नया java.util.HashMap <long, long> (10) मान्य नहीं है। नया लंबा [] [१०] वैध नहीं है, नया लंबा [१०] [] वैध है। वह सामान एक प्रोग्राम लिखता है जो जावा प्रोग्राम लिख सकता है और अधिक कठिन है तो ऐसा लगता है।
कांस्य पुरुष

जवाबों:


703

मुझे बदले में एक प्रश्न पूछना है: क्या आपका GenSet"चेक किया गया" या "अनियंत्रित" है? इसका क्या मतलब है?

  • जाँच की गई : मजबूत टाइपिंगGenSetस्पष्ट रूप से जानता है कि इसमें किस प्रकार की वस्तुएं हैं (अर्थात इसके निर्माता को स्पष्ट रूप से एक Class<E>तर्क के साथ बुलाया गया था , और विधियाँ एक अपवाद को फेंक देंगी जब वे पारित तर्क हैं जो प्रकार के नहीं हैं E। देखें Collections.checkedCollection

    -> उस मामले में, आपको लिखना चाहिए:

    public class GenSet<E> {
    
        private E[] a;
    
        public GenSet(Class<E> c, int s) {
            // Use Array native method to create array
            // of a type only known at run time
            @SuppressWarnings("unchecked")
            final E[] a = (E[]) Array.newInstance(c, s);
            this.a = a;
        }
    
        E get(int i) {
            return a[i];
        }
    }
  • अनियंत्रित : कमजोर टाइपिंग । किसी भी प्रकार की जाँच वास्तव में तर्क के रूप में पारित वस्तुओं में से किसी पर नहीं की जाती है।

    -> उस मामले में, आपको लिखना चाहिए

    public class GenSet<E> {
    
        private Object[] a;
    
        public GenSet(int s) {
            a = new Object[s];
        }
    
        E get(int i) {
            @SuppressWarnings("unchecked")
            final E e = (E) a[i];
            return e;
        }
    }

    ध्यान दें कि सरणी का घटक प्रकार , प्रकार के पैरामीटर का विलोपन होना चाहिए :

    public class GenSet<E extends Foo> { // E has an upper bound of Foo
    
        private Foo[] a; // E erases to Foo, so use Foo[]
    
        public GenSet(int s) {
            a = new Foo[s];
        }
    
        ...
    }

यह सभी परिणाम ज्ञात, और जानबूझकर, जावा में जेनेरिक की कमजोरी: इसे इरेज़र का उपयोग करके लागू किया गया था, इसलिए "जेनेरिक" कक्षाएं यह नहीं जानती हैं कि रन टाइम में वे किस प्रकार के तर्क के साथ बनाए गए थे, और इसलिए टाइप- प्रदान नहीं कर सकते हैं सुरक्षा जब तक कि कुछ स्पष्ट तंत्र (प्रकार-जाँच) लागू न हो।


7
प्रदर्शन-वार सबसे अच्छा विकल्प क्या होगा? मुझे इस सरणी से तत्वों को अक्सर (एक लूप के भीतर) प्राप्त करने की आवश्यकता है। तो एक संग्रह शायद धीमा है, लेकिन इन दोनों में से कौन सबसे तेज है?
user1111929

3
और यदि जेनेरिक प्रकार बाउंड किया गया है, तो बैकिंग सरणी बाउंडिंग प्रकार की होनी चाहिए।
मोर्देखाई

5
@AaronDigulla केवल यह स्पष्ट करने के लिए कि असाइनमेंट नहीं है, लेकिन एक स्थानीय चर का प्रारंभ। आप एक एक्सप्रेशन / स्टेटमेंट को एनोटेट नहीं कर सकते।
kennytm

1
@Varkhan वर्ग कार्यान्वयन के भीतर से इन सरणियों का आकार बदलने का एक तरीका है। उदाहरण के लिए अगर मैं ArrayList की तरह अतिप्रवाह के बाद आकार बदलना चाहता हूं। मैंने Object[] EMPTY_ELEMENTDATA = {}भंडारण के लिए उनके पास ArrayList के कार्यान्वयन को देखा । क्या मैं जेनेरिक के उपयोग के प्रकार को जाने बिना आकार देने के लिए इस तंत्र का उपयोग कर सकता हूं?
जर्नी मैन

2
उन लोगों के लिए जो एक सामान्य प्रकार के साथ एक विधि बनाना चाहते हैं (जो मैं देख रहा था), इसका उपयोग करें:public void <T> T[] newArray(Class<T> type, int length) { ... }
डैनियल केविस्ट

225

तुम यह केर सकते हो:

E[] arr = (E[])new Object[INITIAL_ARRAY_LENGTH];

यह प्रभावी जावा में एक सामान्य संग्रह को लागू करने के सुझाए गए तरीकों में से एक है ; आइटम 26 । किसी भी प्रकार की त्रुटियां, बार-बार सरणी डालने की आवश्यकता नहीं है। हालांकि यह एक चेतावनी को ट्रिगर करता है क्योंकि यह संभावित रूप से खतरनाक है, और सावधानी के साथ उपयोग किया जाना चाहिए। जैसा कि टिप्पणियों में विस्तृत है, यह Object[]अब हमारे E[]प्रकार के रूप में सामने आ रहा है , और ClassCastExceptionयदि अनपेक्षित रूप से उपयोग किया जाता है तो अप्रत्याशित त्रुटियां हो सकती हैं ।

अंगूठे के एक नियम के रूप में, यह व्यवहार तब तक सुरक्षित है जब तक कि डाली सरणी का आंतरिक रूप से उपयोग किया जाता है (जैसे डेटा संरचना को वापस करने के लिए), और ग्राहक कोड को वापस नहीं किया जाता है या उजागर नहीं किया जाता है। क्या आपको जेनेरिक प्रकार की एक सरणी को अन्य कोड में वापस करने की आवश्यकता है, जिस प्रतिबिंब Arrayवर्ग का आप उल्लेख करते हैं वह जाने का सही तरीका है।


यह उल्लेख करते हुए कि जहाँ भी संभव हो, यदि आपके पास Listजेनेरिक का उपयोग किया जा रहा है, तो सरणियों के बजाय एस के साथ काम करने में आपको अधिक खुशी होगी । निश्चित रूप से कभी-कभी आपके पास कोई विकल्प नहीं होता है, लेकिन संग्रह ढांचे का उपयोग करना कहीं अधिक मजबूत होता है।


47
यह कार्य नहीं करेगा यदि सरणी को किसी भी प्रकार के टाइप किए गए सरणी के रूप में माना जाता है, जैसे String[] s=b;कि उपरोक्त test()विधि में। ऐसा इसलिए है क्योंकि E की सरणी वास्तव में नहीं है, यह वस्तु है []। यदि आप चाहते हैं तो यह मायने रखता है, जैसे List<String>[]- आप इसके Object[]लिए उपयोग नहीं कर सकते हैं , आपके पास List[]विशेष रूप से होना चाहिए । यही कारण है कि आपको परिलक्षित वर्ग <?> सरणी निर्माण का उपयोग करने की आवश्यकता है।
लॉरेंस डॉल

8
कोने-मामले / समस्या यदि आप करना चाहते हैं, उदाहरण के लिए, public E[] toArray() { return (E[])internalArray.clone(); }जब internalArrayटाइप किया जाता है E[], और इसलिए वास्तव में ए Object[]। यह टाइप-कास्ट अपवाद के साथ रनटाइम पर विफल रहता है क्योंकि Object[]जो भी प्रकार Eहोता है उसे किसी सरणी में असाइन नहीं किया जा सकता है।
लॉरेंस Dol

17
मूल रूप से, यह दृष्टिकोण तब तक काम करेगा जब तक आप सरणी को वापस नहीं करते हैं या इसे पास नहीं करते हैं या इसे कक्षा के बाहर किसी स्थान पर संग्रहीत करते हैं जिसके लिए एक निश्चित प्रकार के सरणी की आवश्यकता होती है। जब तक आप क्लास के अंदर हैं तब तक आप ठीक हैं क्योंकि E मिट चुका है। यह "खतरनाक" है क्योंकि यदि आप इसे या कुछ वापस करने की कोशिश करते हैं, तो आपको कोई चेतावनी नहीं मिलती है कि यह असुरक्षित है। लेकिन अगर आप सावधान हैं तो यह काम करता है।
22

3
यह काफी सुरक्षित है। में E[] b = (E[])new Object[1];आप स्पष्ट रूप से देख सकते हैं बनाया सरणी के लिए केवल संदर्भ है कि bऔर किस प्रकार का bहै E[]। इसलिए एक अलग प्रकार के एक भिन्न चर के माध्यम से गलती से एक ही सरणी तक पहुंचने का आपको कोई खतरा नहीं है। यदि इसके बजाय, आपके पास था Object[] a = new Object[1]; E[]b = (E[])a; तो आपको इस बात से अवगत होना होगा कि आप कैसे उपयोग करते हैं a
हारून मैकडैड

5
कम से कम जावा 1.6 में, यह एक चेतावनी उत्पन्न करता है: "ऑब्जेक्ट से अनियंत्रित कास्ट [] से टी []"
क्वांटम 7

61

प्रकार की सुरक्षा को संरक्षित करते समय आप जिस प्रकार की तलाश कर रहे हैं, उसका एक प्रकार प्राप्त करने के लिए जेनेरिक का उपयोग कैसे करें (जैसा कि अन्य उत्तरों के विपरीत है, जो या तो आपको एक Objectसरणी देगा या संकलन समय पर चेतावनी में परिणाम देगा):

import java.lang.reflect.Array;  

public class GenSet<E> {  
    private E[] a;  

    public GenSet(Class<E[]> clazz, int length) {  
        a = clazz.cast(Array.newInstance(clazz.getComponentType(), length));  
    }  

    public static void main(String[] args) {  
        GenSet<String> foo = new GenSet<String>(String[].class, 1);  
        String[] bar = foo.a;  
        foo.a[0] = "xyzzy";  
        String baz = foo.a[0];  
    }  
}

यह चेतावनी के बिना संकलित करता है, और जैसा कि आप देख सकते हैं main, जिस प्रकार के लिए आप एक उदाहरण की घोषणा करते हैं GenSet, आप aउस प्रकार के एक सरणी को असाइन कर सकते हैं , और आप aउस प्रकार के एक चर से एक तत्व असाइन कर सकते हैं , जिसका अर्थ है कि सरणी और सरणी में मान सही प्रकार के हैं।

यह जावा ट्यूटोरियल में चर्चा के अनुसार रनटाइम टाइप टोकन के रूप में वर्ग शाब्दिक का उपयोग करके काम करता है । कक्षा शाब्दिकों को संकलक द्वारा उदाहरण के रूप में माना जाता है java.lang.Class। एक का उपयोग करने के लिए, बस एक वर्ग के नाम का पालन करें .class। इसलिए, कक्षा का प्रतिनिधित्व करने वाली String.classएक Classवस्तु के रूप में कार्य करता है String। यह इंटरफेस, एनम, किसी भी आयामी सरणियों (जैसे String[].class), आदिम (जैसे int.class), और कीवर्ड void(यानी void.class) के लिए भी काम करता है ।

Classखुद जेनेरिक (के रूप में घोषित है Class<T>, जहां Tप्रकार है कि के लिए खड़ा Classवस्तु का प्रतिनिधित्व किया जाता है), जिसका अर्थ है कि के प्रकार String.classहै Class<String>

इसलिए, जब भी आप कंस्ट्रक्टर को कॉल करते हैं, तो आप GenSetपहले तर्क के लिए एक क्लास शाब्दिक में पास करते हैं, जो कि एरे GenSetके घोषित प्रकार (उदाहरण के String[].classलिए GenSet<String>) के एक सरणी का प्रतिनिधित्व करता है । ध्यान दें कि आप प्राथमिकताओं का एक सरणी प्राप्त करने में सक्षम नहीं होंगे, क्योंकि टाइप चर के लिए आदिम का उपयोग नहीं किया जा सकता है।

कंस्ट्रक्टर के अंदर, विधि को कॉल करके castपास किए गए Objectतर्क को कक्षा में उस Classऑब्जेक्ट द्वारा प्रतिनिधित्व किया जाता है जिस पर विधि को बुलाया गया था। रिटर्न newInstanceमें स्थिर विधि को ऑब्जेक्ट के द्वारा दर्शाए गए प्रकार के एक सरणी के रूप में पहले तर्क के रूप में और दूसरे तर्क के रूप में पारित लंबाई द्वारा निर्दिष्ट लंबाई के java.lang.reflect.Arrayरूप में कॉल करना । विधि कॉलिंग रिटर्न एक वस्तु सरणी के घटक प्रकार का प्रतिनिधित्व करने का प्रतिनिधित्व करती वस्तु जिस पर विधि बुलाया गया था (जैसे के लिए , यदि वस्तु एक सरणी का प्रतिनिधित्व नहीं करता है)।ObjectClassintgetComponentTypeClassClassString.classString[].classnullClass

यह अंतिम वाक्य पूरी तरह से सही नहीं है। कॉलिंग String[].class.getComponentType()एक Classवस्तु को वर्ग का प्रतिनिधित्व करता है String, लेकिन इसका प्रकार है Class<?>, ऐसा नहीं है Class<String>, यही कारण है कि आप निम्न की तरह कुछ नहीं कर सकते।

String foo = String[].class.getComponentType().cast("bar"); // won't compile

उसी में प्रत्येक विधि के लिए Classएक Classवस्तु देता है ।

इस जवाब पर जोआचिम सॉयर की टिप्पणी के बारे में (मेरे पास इस पर टिप्पणी करने के लिए पर्याप्त प्रतिष्ठा नहीं है), उदाहरण के लिए कलाकारों का उपयोग T[]एक चेतावनी के परिणामस्वरूप होगा क्योंकि कंपाइलर उस मामले में प्रकार की सुरक्षा की गारंटी नहीं दे सकता है।


इंगो की टिप्पणियों के बारे में संपादित करें:

public static <T> T[] newArray(Class<T[]> type, int size) {
   return type.cast(Array.newInstance(type.getComponentType(), size));
}

5
यह बेकार है, यह केवल एक नया स्ट्रिंग लिखने का जटिल तरीका है [...]। लेकिन वास्तव में जिस चीज की जरूरत होती है वह है सार्वजनिक स्थैतिक <T> T [] newArray (int size) {...}, और यह केवल जावा नोयर में मौजूद नहीं है, इसे प्रतिबिंब के साथ जोड़ा जा सकता है - इसका कारण यह है कि कैसे के बारे में जानकारी एक सामान्य प्रकार त्वरित है रनटाइम पर उपलब्ध नहीं है।
इंगो

4
@ इंगो आप किस बारे में बात कर रहे हैं? मेरे कोड का उपयोग किसी भी प्रकार की एक सरणी बनाने के लिए किया जा सकता है।
गदजोन

3
@Charlatan: ज़रूर, लेकिन इतना नया [] हो सकता है। सवाल यह है: कौन जानता है कि किस प्रकार और कब। इसलिए, यदि आपके पास एक सामान्य प्रकार है, तो आप नहीं कर सकते।
इंगो

2
मुझे उस पर संदेह नहीं है। मुद्दा यह है कि, आपको जेनेरिक प्रकार X के लिए रनटाइम पर एक क्लास ऑब्जेक्ट नहीं मिलता है।
इंगो

2
लगभग। मैं स्वीकार करता हूं कि यह नए [] के साथ हासिल किया जा सकता है। व्यवहार में, यह लगभग हमेशा काम करेगा। हालाँकि, यह अभी भी संभव नहीं है, उदाहरण के लिए, E के साथ परिचालित एक कंटेनर वर्ग लिखने के लिए जिसमें E []] toArray () विधि है और जो वास्तव में एक E [] सरणी देता है। आपका कोड तभी लागू किया जा सकता है जब संग्रह में कम से कम एक ई-ऑब्जेक्ट हो। तो, एक सामान्य समाधान असंभव है।
इंगो

42

यह एकमात्र उत्तर है जो सुरक्षित है

E[] a;

a = newArray(size);

@SafeVarargs
static <E> E[] newArray(int length, E... array)
{
    return Arrays.copyOf(array, length);
}

मुझे इसे देखना था, लेकिन हाँ, दूसरा "लंबाई" तर्क Arrays#copyOf()पहले तर्क के रूप में दिए गए सरणी की लंबाई से स्वतंत्र है। यह चतुर है, हालांकि यह कॉल की लागत का भुगतान करता है Math#min()और System#arrayCopy(), जिनमें से किसी को भी इस काम को करने के लिए कड़ाई से आवश्यक नहीं है। docs.oracle.com/javase/7/docs/api/java/util/…
seh

8
यह काम नहीं करता है यदि Eएक प्रकार चर है। Eजब Eएक प्रकार का चर होता है, तो varargs एक एरेस बनाता है, जिससे यह बहुत अलग नहीं होता है (E[])new Object[n]। कृपया http://ideone.com/T8xF91 देखें । यह किसी भी अन्य उत्तर की तुलना में अधिक प्रकार से सुरक्षित नहीं है
रेडियोएडफ़

1
@ रोडीफ़ - समाधान संकलित समय पर काफी सुरक्षित है। ध्यान दें कि मिटाना भाषा की कल्पना का हिस्सा नहीं है; युक्ति को सावधानीपूर्वक लिखा गया है ताकि हम भविष्य में पूर्ण रूप से संशोधन कर सकें - और फिर यह समाधान अन्य समाधानों के विपरीत, रनटाइम पर भी पूरी तरह से काम करेगा।
झोंगयू

@ रोडीफ - यह बहस का विषय है कि क्या जेनेरिक सरणी निर्माण पर प्रतिबंध लगाना एक अच्छा विचार है। इसकी परवाह किए बिना, भाषा एक पिछले दरवाजे को छोड़ देती है - वैरग को जेनेरिक सरणी निर्माण की आवश्यकता होती है। यह उतना ही अच्छा है जितना कि भाषा ने अनुमति दी है new E[]। आपके द्वारा अपने उदाहरण में दिखाई गई समस्या एक सामान्य समस्या है, जो इस प्रश्न और इस उत्तर के लिए अद्वितीय नहीं है।
झोंगयू

2
@ रोडीफ़ - कुछ अंतर हैं। इस समाधान की शुद्धता संकलक द्वारा जाँच की जाती है; यह मजबूर कलाकारों के मानवीय तर्क पर निर्भर नहीं करता है। अंतर इस विशेष समस्या के लिए महत्वपूर्ण नहीं है। कुछ लोग थोड़े से फैंसी होना पसंद करते हैं, बस इतना ही। यदि किसी को ओपी के शब्दों द्वारा गुमराह किया जाता है, तो यह आपकी टिप्पणियों और मेरा द्वारा स्पष्ट किया जाता है।
झोंगयू

33

अधिक आयामों का विस्तार करने के लिए, बस ( और एक प्रकार का पैरामीटर है, एक है , पूर्णांक के माध्यम से ) []पैरामीटर आयाम जोड़ें :newInstance()TclsClass<T>d1d5

T[] array = (T[])Array.newInstance(cls, d1);
T[][] array = (T[][])Array.newInstance(cls, d1, d2);
T[][][] array = (T[][][])Array.newInstance(cls, d1, d2, d3);
T[][][][] array = (T[][][][])Array.newInstance(cls, d1, d2, d3, d4);
T[][][][][] array = (T[][][][][])Array.newInstance(cls, d1, d2, d3, d4, d5);

Array.newInstance()विवरण के लिए देखें।


4
+1 बहु-आयामी सरणी निर्माण के बारे में प्रश्न हैं जो इस पद के द्वंद्व के रूप में बंद हो जाते हैं - लेकिन किसी भी उत्तर ने विशेष रूप से संबोधित नहीं किया था।
पॉल बेलोरा

1
@ जोर्डन सी शायद; हालाँकि यह स्पिरिटओवरफ्लो . com/a/5671304/ 616460 के रूप में आत्मा में समान है ; मैं कल को संभालने के सर्वोत्तम तरीके के बारे में सोचूंगा। मुझे नींद आ रही है।
जेसन सी

14

जावा 8 में, हम लैम्बडा या मेथड रेफरेंस का उपयोग करके एक प्रकार की जेनेरिक ऐरे क्रिएशन कर सकते हैं। यह चिंतनशील दृष्टिकोण (जो पास से गुजरता है Class) के समान है, लेकिन यहां हम प्रतिबिंब का उपयोग नहीं कर रहे हैं।

@FunctionalInterface
interface ArraySupplier<E> {
    E[] get(int length);
}

class GenericSet<E> {
    private final ArraySupplier<E> supplier;
    private E[] array;

    GenericSet(ArraySupplier<E> supplier) {
        this.supplier = supplier;
        this.array    = supplier.get(10);
    }

    public static void main(String[] args) {
        GenericSet<String> ofString =
            new GenericSet<>(String[]::new);
        GenericSet<Double> ofDouble =
            new GenericSet<>(Double[]::new);
    }
}

उदाहरण के लिए, यह द्वारा प्रयोग किया जाता है <A> A[] Stream.toArray(IntFunction<A[]>)

यह हो सकता है भी गुमनाम वर्गों का उपयोग पूर्व जावा 8 से किया जाना है, लेकिन यह अधिक बोझिल है।


आपको वास्तव में इसके लिए एक विशेष इंटरफ़ेस की आवश्यकता नहीं है ArraySupplier, आप कंस्ट्रक्टर को घोषित कर सकते हैं GenSet(Supplier<E[]> supplier) { ...और उसे उसी पंक्ति के साथ कॉल कर सकते हैं जैसे आपके पास है।
लीई

4
@ मेरे उदाहरण के समान होने के लिए, यह होगा IntFunction<E[]>, लेकिन हाँ यह सच है।
रेडियोडेफ

11

यह प्रभावी जावा, 2 डी संस्करण , आइटम 25 के अध्याय 5 (जेनरिक) में शामिल है ... सरणियों की सूचियों को प्राथमिकता दें

आपका कोड काम करेगा, हालांकि यह एक अनियंत्रित चेतावनी उत्पन्न करेगा (जिसे आप निम्नलिखित एनोटेशन के साथ दबा सकते हैं:

@SuppressWarnings({"unchecked"})

हालांकि, शायद एक ऐरे के बजाय एक सूची का उपयोग करना बेहतर होगा।

OpenJDK प्रोजेक्ट साइट पर इस बग / सुविधा की एक दिलचस्प चर्चा है ।


8

आपको कंस्ट्रक्टर को क्लास तर्क पास करने की आवश्यकता नहीं है। इसे इस्तेमाल करे।

public class GenSet<T> {
    private final T[] array;
    @SuppressWarnings("unchecked")
    public GenSet(int capacity, T... dummy) {
        if (dummy.length > 0)
            throw new IllegalArgumentException(
              "Do not provide values for dummy argument.");
        Class<?> c = dummy.getClass().getComponentType();
        array = (T[])Array.newInstance(c, capacity);
    }
    @Override
    public String toString() {
        return "GenSet of " + array.getClass().getComponentType().getName()
            + "[" + array.length + "]";
    }
}

तथा

GenSet<Integer> intSet = new GenSet<>(3);
System.out.println(intSet);
System.out.println(new GenSet<String>(2));

परिणाम:

GenSet of java.lang.Integer[3]
GenSet of java.lang.String[2]

7

जावा जेनरिक संकलन समय पर प्रकारों की जाँच करके और उपयुक्त कास्ट डालने के द्वारा काम करता है, लेकिन संकलित फाइलों में टाइपों को मिटा देता है। यह कोड द्वारा सामान्य पुस्तकालयों को प्रयोग करने योग्य बनाता है जो जेनेरिक (जो एक जानबूझकर डिजाइन निर्णय था) को समझ में नहीं आता है, लेकिन इसका मतलब है कि आप सामान्य रूप से यह पता नहीं लगा सकते हैं कि टाइप रन समय पर क्या है।

सार्वजनिक Stack(Class<T> clazz,int capacity)निर्माता रन टाइम पर एक कक्षा वस्तु है, जो साधन कक्षा की जानकारी पारित करने के लिए आवश्यक है कि आप है कोड इसकी आवश्यकता है कि करने के लिए रनटाइम पर उपलब्ध। और Class<T>फॉर्म का मतलब है कि संकलक यह जांच करेगा कि आपके द्वारा पास की गई क्लास ऑब्जेक्ट टी के लिए क्लास ऑब्जेक्ट ठीक है। टी का उपवर्ग नहीं, टी का सुपरक्लास नहीं, बल्कि ठीक टी।

इसका मतलब यह है कि आप अपने निर्माता में उपयुक्त प्रकार की एक सरणी ऑब्जेक्ट बना सकते हैं, जिसका अर्थ है कि आपके संग्रह में आपके द्वारा संग्रहित वस्तुओं के प्रकार को उनके प्रकार की जाँच की जाएगी, जिस बिंदु पर उन्हें संग्रह में जोड़ा गया है।


6

हाय यद्यपि धागा मर चुका है, मैं आपका ध्यान इस ओर आकर्षित करना चाहूंगा:

संकलन समय के दौरान सामान्य जाँच के लिए जेनरिक का उपयोग किया जाता है:

  • इसलिए उद्देश्य यह जांचना है कि जो आता है वह आपकी आवश्यकता है।
  • आप क्या लौटाते हैं, उपभोक्ता को क्या चाहिए।
  • इसे देखो:

यहां छवि विवरण दर्ज करें

जब आप सामान्य वर्ग लिख रहे हों तो टाइपकास्टिंग चेतावनियों के बारे में चिंता न करें। चिंता करें जब आप इसका उपयोग कर रहे हैं।


6

इस समाधान के बारे में क्या?

@SafeVarargs
public static <T> T[] toGenericArray(T ... elems) {
    return elems;
}

यह काम करता है और सच होने के लिए बहुत सरल लगता है। क्या कोई कमी है?


3
नीट, लेकिन केवल तभी काम करता है जब आप इसे 'मैन्युअल' कहते हैं, अर्थात तत्वों को व्यक्तिगत रूप से पास करते हैं। यदि आप एक नया उदाहरण नहीं बना सकते हैं T[], तो आप प्रोग्राम T[] elemsमें पास होने के लिए प्रोग्राम नहीं बना सकते हैं । और यदि आप कर सकते हैं, तो आपको फ़ंक्शन की आवश्यकता नहीं होगी।
orlade

5

इस कोड को भी देखें:

public static <T> T[] toArray(final List<T> obj) {
    if (obj == null || obj.isEmpty()) {
        return null;
    }
    final T t = obj.get(0);
    final T[] res = (T[]) Array.newInstance(t.getClass(), obj.size());
    for (int i = 0; i < obj.size(); i++) {
        res[i] = obj.get(i);
    }
    return res;
}

यह किसी भी प्रकार के ऑब्जेक्ट की सूची को उसी प्रकार की एक सरणी में परिवर्तित करता है।


हां, आप अशक्त हैं, जो अपेक्षित खाली सरणी नहीं है। यह सबसे अच्छा आप कर सकते हैं, लेकिन आदर्श नहीं है।
केविन कॉक्स

यह भी विफल हो सकता है अगर Listइसमें एक से अधिक प्रकार की वस्तु है जैसे toArray(Arrays.asList("abc", new Object()))कि फेंक देंगे ArrayStoreException
रेडियोडफ

मैं इस के एक छीन नीचे संस्करण का इस्तेमाल किया; पहली बात यह है कि मैं काम करने के लिए सक्षम था, हालांकि माना जाता है कि मैंने कुछ अधिक शामिल समाधानों की कोशिश नहीं की। एक forलूप और दूसरों से बचने के लिए, Arrays.fill(res, obj);क्योंकि मैं प्रत्येक सूचकांक के लिए समान मूल्य चाहता था।
फरवरी को bbarker

5

मुझे एक त्वरित और आसान तरीका मिला है जो मेरे लिए काम करता है। ध्यान दें कि मैंने केवल जावा JDK 8 पर इसका उपयोग किया है। मुझे नहीं पता कि यह पिछले संस्करणों के साथ काम करेगा या नहीं।

यद्यपि हम एक विशिष्ट प्रकार के पैरामीटर के जेनेरिक सरणी को नहीं रोक सकते हैं, हम जेनेरिक क्लास कंस्ट्रक्टर के लिए पहले से बनाए गए सरणी को पास कर सकते हैं।

class GenArray <T> {
    private T theArray[]; // reference array

    // ...

    GenArray(T[] arr) {
        theArray = arr;
    }

    // Do whatever with the array...
}

अब मुख्य में हम इस तरह सरणी बना सकते हैं:

class GenArrayDemo {
    public static void main(String[] args) {
        int size = 10; // array size
        // Here we can instantiate the array of the type we want, say Character (no primitive types allowed in generics)
        Character[] ar = new Character[size];

        GenArray<Character> = new Character<>(ar); // create the generic Array

        // ...

    }
}

अपने सरणियों के साथ अधिक लचीलेपन के लिए आप एक लिंक की गई सूची का उपयोग कर सकते हैं। ArrayList और अन्य विधियाँ Java.util.ArrayList वर्ग में पाई जाती हैं।


4

सरणी बनाने के लिए उदाहरण जावा प्रतिबिंब का उपयोग कर रहा है। ऐसा करना आमतौर पर अनुशंसित नहीं है, क्योंकि यह टाइपसेफ़ नहीं है। इसके बजाय, आपको जो करना चाहिए वह सिर्फ एक आंतरिक सूची का उपयोग करना है, और सरणी से बचें।


13
दूसरे उदाहरण (Array.newInstance का उपयोग कर ()) है तथ्य typesafe में। यह संभव है क्योंकि क्लास ऑब्जेक्ट के प्रकार टी को सरणी के टी से मेल खाना चाहिए। यह मूल रूप से आपको जानकारी प्रदान करने के लिए मजबूर करता है कि जावा रनटाइम जेनेरिक के लिए त्याग करता है।
जोकिम सॉयर


3

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

Object attributeValue = null;
try {
    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }
    else if(!clazz.isInterface()){
        attributeValue = BeanUtils.instantiateClass(clazz);
    }
} catch (Exception e) {
    logger.debug("Cannot instanciate \"{}\"", new Object[]{clazz});
}

इस सेगमेंट पर ध्यान दें:

    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }

सरणी आरंभ करने के लिए जहां Array.newInstance (सरणी का वर्ग, सरणी का आकार) । कक्षा आदिम (int.class) और ऑब्जेक्ट (Integer.class) दोनों हो सकती है।

बीनटिल्स स्प्रिंग का हिस्सा है।


3

वास्तव में ऐसा करने का एक आसान तरीका है, वस्तुओं की एक सरणी बनाना और इसे अपने इच्छित प्रकार में डालना जैसे कि निम्न उदाहरण:

T[] array = (T[])new Object[SIZE];

जहां SIZEएक स्थिरांक है और Tएक प्रकार का पहचानकर्ता है


1

अन्य लोगों द्वारा सुझाए गए मजबूर कलाकारों ने मेरे लिए काम नहीं किया, अवैध कास्टिंग के अपवाद को फेंक दिया।

हालांकि, इस निहित कलाकारों ने ठीक काम किया:

Item<K>[] array = new Item[SIZE];

जहां आइटम एक वर्ग है जिसे मैंने सदस्य के रूप में परिभाषित किया है:

private K value;

इस तरह से आपको K का एक प्रकार मिलता है (यदि आइटम में केवल मान है) या कोई सामान्य प्रकार जिसे आप वर्ग में परिभाषित करना चाहते हैं।


1

आपके द्वारा पोस्ट किए गए उदाहरण में क्या चल रहा है, इस सवाल का जवाब किसी और ने नहीं दिया है।

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

जैसा कि दूसरों ने कहा है कि संकलन के दौरान जेनरिक "मिट" रहे हैं। इसलिए रनटाइम के दौरान जेनेरिक का एक उदाहरण नहीं जानता कि इसका घटक प्रकार क्या है। इसका कारण ऐतिहासिक है, सूर्य मौजूदा इंटरफ़ेस (स्रोत और बाइनरी दोनों) को तोड़ने के बिना जेनरिक जोड़ना चाहते थे।

दूसरी ओर सरणी करते क्रम में अपने घटक प्रकार पता।

यह उदाहरण समस्या के चारों ओर काम करता है जिसमें कोड होता है जो निर्माता को कॉल करता है (जो प्रकार जानता है) एक पैरामीटर पास करता है जो कक्षा को आवश्यक प्रकार बताता है।

तो अनुप्रयोग वर्ग का निर्माण कुछ इस तरह से करेगा

Stack<foo> = new Stack<foo>(foo.class,50)

और कंस्ट्रक्टर अब जानता है (रनटाइम पर) घटक प्रकार क्या है और प्रतिबिंब एपीआई के माध्यम से सरणी के निर्माण के लिए उस जानकारी का उपयोग कर सकता है।

Array.newInstance(clazz, capacity);

अंत में हमारे पास एक टाइप कास्ट है क्योंकि कंपाइलर के पास यह जानने का कोई तरीका नहीं है कि जिस सरणी से लौटा गया Array#newInstance()है वह सही प्रकार है (भले ही हम जानते हों)।

यह शैली थोड़ी बदसूरत है, लेकिन यह कभी-कभी जेनेरिक प्रकार बनाने के लिए सबसे कम बुरा समाधान हो सकता है, जो किसी भी कारण से रनटाइम पर उनके घटक प्रकार को जानने की आवश्यकता होती है (सरणियों का निर्माण, या उनके घटक प्रकार के उदाहरणों को बनाने के लिए, आदि)।


1

मुझे इस समस्या के आसपास एक तरह का काम मिला।

नीचे दी गई लाइन जेनेरिक एरे क्रिएशन एरर को फेंकती है

List<Person>[] personLists=new ArrayList<Person>()[10];

हालांकि अगर मैं List<Person>एक अलग वर्ग में इनकैप्सुलेट करता हूं , तो यह काम करता है।

import java.util.ArrayList;
import java.util.List;


public class PersonList {

    List<Person> people;

    public PersonList()
    {
        people=new ArrayList<Person>();
    }
}

आप क्लास में मौजूद लोगों को एक गेटटर के माध्यम से बेनकाब कर सकते हैं। नीचे दी गई लाइन आपको एक एरे देगी, जिसमें List<Person>हर एलीमेंट है। के दूसरे शब्दों सरणी में List<Person>

PersonList[] personLists=new PersonList[10];

मुझे कुछ इस तरह की आवश्यकता थी कुछ कोड पर मैं काम कर रहा था और यह वही है जो मैंने इसे काम करने के लिए प्राप्त किया। अब तक कोई समस्या नहीं।


0

आप एक ऑब्जेक्ट ऐरे बना सकते हैं और इसे हर जगह E पर कास्ट कर सकते हैं। हाँ, यह करने के लिए बहुत साफ तरीका नहीं है लेकिन इसे कम से कम काम करना चाहिए।


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

बीयूटी जो कुछ मामलों में जीता है जैसे कि यदि आपका सामान्य वर्ग तुलनात्मक इंटरफ़ेस लागू करना चाहता है।
रामप्रसाद बिस्मिल

सात साल पहले आपका स्वागत है, मुझे लगता है।
Esko

1
यदि आप सामान्य कोड से गैर-जेनेरिक कॉलर में सरणी को वापस करने का प्रयास करते हैं तो यह काम नहीं करेगा। इसमें हेड-स्क्रैचिंग क्लासकास्टीसेप्शन होगा।
प्लग

0

इसे इस्तेमाल करे।

private int m = 0;
private int n = 0;
private Element<T>[][] elements = null;

public MatrixData(int m, int n)
{
    this.m = m;
    this.n = n;

    this.elements = new Element[m][n];
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < n; j++)
        {
            this.elements[i][j] = new Element<T>();
        }
    }
}

मुझे आपका कोड नहीं मिल रहा है, आपकी Elementकक्षा कहां से आती है?

0

इसके लिए एक आसान, गंदा गड़बड़झाला आपके मुख्य वर्ग के अंदर एक दूसरे "धारक" वर्ग को घोंसला बनाने के लिए होगा, और इसका उपयोग आपके डेटा को रखने के लिए करेगा।

public class Whatever<Thing>{
    private class Holder<OtherThing>{
        OtherThing thing;
    }
    public Holder<Thing>[] arrayOfHolders = new Holder<Thing>[10]
}

3
यह वास्तव में काम नहीं करता है। new Holder<Thing>[10]एक सामान्य सरणी निर्माण है।
रेडियोडेफ

0

हो सकता है कि इस प्रश्न से कोई संबंध न हो लेकिन जब मुझे generic array creationउपयोग करने के लिए " " त्रुटि मिल रही थी

Tuple<Long,String>[] tupleArray = new Tuple<Long,String>[10];

मैं निम्नलिखित कार्यों का पता लगाता हूं (और मेरे लिए काम किया है) @SuppressWarnings({"unchecked"}):

 Tuple<Long, String>[] tupleArray = new Tuple[10];

हाँ, यह काफी संबंधित नहीं है, लेकिन समान मुद्दों में निहित है (मिटाना, सरणी कोवरियन)। यहाँ पैरामीटर किए गए प्रकारों के सरणियाँ बनाने के बारे में एक पोस्ट का एक उदाहरण दिया गया है: stackoverflow.com/questions/9542076/…
पॉल बेलोरा

0

मैं सोच रहा हूँ कि क्या यह कोड एक प्रभावी सामान्य सरणी बनाएगा?

public T [] createArray(int desiredSize){
    ArrayList<T> builder = new ArrayList<T>();
    for(int x=0;x<desiredSize;x++){
        builder.add(null);
    }
    return builder.toArray(zeroArray());
}

//zeroArray should, in theory, create a zero-sized array of T
//when it is not given any parameters.

private T [] zeroArray(T... i){
    return i;
}

संपादित करें: शायद इस तरह की एक सरणी बनाने का एक वैकल्पिक तरीका, यदि आपके लिए आवश्यक आकार ज्ञात था और छोटा था, तो शून्यआयर्रे कमांड में "नल" की आवश्यक संख्या को बस फीड करना होगा?

हालांकि स्पष्ट रूप से यह createArray कोड का उपयोग करने के रूप में बहुमुखी नहीं है।


नहीं, यह काम नहीं करता है। Tजब Tएक प्रकार का चर होता है, तो varargs मिटता है , अर्थात zeroArrayरिटर्न देता है Object[]। देखें http://ideone.com/T8xF91
रेडियोडफ़

0

आप एक कलाकार का उपयोग कर सकते हैं:

public class GenSet<Item> {
    private Item[] a;

    public GenSet(int s) {
        a = (Item[]) new Object[s];
    }
}

यदि आप यह सुझाव देने जा रहे हैं, तो आपको वास्तव में इसकी सीमाओं को समझाने की आवश्यकता है। कभी aभी कक्षा के बाहर उजागर न करें !
रेडियोडफ

0

मैं वास्तव में एक सामान्य सरणी शुरू करने में असमर्थता को बायपास करने के लिए एक बहुत ही अनूठा समाधान पाया। आपको जो करना है वह एक ऐसा वर्ग बनाना है जो जेनेरिक चर T में लेता है जैसे:

class GenericInvoker <T> {
    T variable;
    public GenericInvoker(T variable){
        this.variable = variable;
    }
}

और फिर आपके एरे क्लास में बस इतना ही शुरू होना चाहिए:

GenericInvoker<T>[] array;
public MyArray(){
    array = new GenericInvoker[];
}

एक शुरू करने new Generic Invoker[]से अनियंत्रित एक समस्या होगी, लेकिन वास्तव में कोई भी मुद्दा नहीं होना चाहिए।

सरणी से प्राप्त करने के लिए आपको सरणी [i] को कॉल करना चाहिए।

public T get(int index){
    return array[index].variable;
}

बाकी, जैसे कि सरणी का आकार बदलना Arrays.copyOf () के साथ किया जा सकता है:

public void resize(int newSize){
    array = Arrays.copyOf(array, newSize);
}

और ऐड फंक्शन को इस तरह जोड़ा जा सकता है:

public boolean add(T element){
    // the variable size below is equal to how many times the add function has been called 
    // and is used to keep track of where to put the next variable in the array
    arrays[size] = new GenericInvoker(element);
    size++;
}

1
प्रश्न सामान्य प्रकार के पैरामीटर के प्रकार का एक सरणी बनाने के बारे में था T, न कि कुछ पैरामीटर प्रकार के सरणी के बारे में।
सोतिरियोस डेलिमोनोलिस

यह हालांकि एक ही कार्य पूरा करता है और आपको अपने कस्टम संग्रह को उपयोग करने में आसान बनाने वाली कक्षा में धकेलने की आवश्यकता नहीं है।
क्रेब नेबुला

क्या काम ? यह वस्तुतः एक अलग कार्य है: एक पैरामीटराइज़्ड प्रकार की एक सरणी बनाम जेनेरिक प्रकार पैरामीटर की एक सरणी।
सोतिरियोस डेलिमोनोलिस

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

मैं सोत्रोस से सहमत हूं। जवाब सोचने के दो तरीके हैं। या तो यह एक अलग प्रश्न का उत्तर है, या यह प्रश्न को सामान्य बनाने का प्रयास है। दोनों गलत / सहायक नहीं हैं। जो लोग "जेनेरिक एरे" क्लास को लागू करने के बारे में मार्गदर्शन के लिए देख रहे हैं, जब वे प्रश्न शीर्षक पढ़ते हैं तो पढ़ना बंद कर देंगे। और जब वे 30 उत्तरों के साथ एक क्यू पाते हैं, तो वे अंत तक स्क्रॉल करने की संभावना नहीं रखते हैं और एक एसओ नवागंतुक से शून्य वोट उत्तर पढ़ते हैं।
स्टीफन सी।

0

Vnportnoy के अनुसार वाक्य रचना

GenSet<Integer> intSet[] = new GenSet[3];

के रूप में भरा जा करने के लिए, शून्य संदर्भों की एक सरणी बनाता है

for (int i = 0; i < 3; i++)
{
   intSet[i] = new GenSet<Integer>();
}

जो सुरक्षित है।


-1
private E a[];
private int size;

public GenSet(int elem)
{
    size = elem;
    a = (E[]) new E[size];
}

आपको हमेशा अपने कोड में एक स्पष्टीकरण जोड़ना चाहिए, और यह बताना चाहिए कि यह मूल पोस्ट किए गए प्रश्न को क्यों हल करता है।
मज़ारेज़

-1

जेनेरिक सरणी निर्माण जावा में अस्वीकृत है, लेकिन आप इसे पसंद कर सकते हैं

class Stack<T> {
private final T[] array;
public Stack(int capacity) {
    array = (T[]) new Object[capacity];
 }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.