जावा की जेनरिक में क्या गलत है? [बन्द है]


49

मैंने इस साइट पोस्ट पर कई बार देखा है जो जावा के जेनरिक के कार्यान्वयन को कम करता है। अब, मैं ईमानदारी से कह सकता हूं कि मेरे पास उनके उपयोग के साथ कोई समस्या नहीं है। हालाँकि, मैंने स्वयं एक सामान्य वर्ग बनाने का प्रयास नहीं किया है। तो, जावा के सामान्य समर्थन के साथ आपके मुद्दे क्या हैं?




क्लिंटन के रूप में शुरू ("iBatis आदमी") ने कहा, "वे काम नहीं करते ..."
gnat

जवाबों:


54

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


4
बस जोड़ने के लिए, प्रकार के क्षरण के कारण, रनटाइम पर सामान्य जानकारी प्राप्त करना तब तक संभव नहीं है जब तक कि आप TypeLiteral google-guice.googlecode.com/svn/trunk/javadoc/com/google/inject/…
जेरेमी

43
मतलब यह है किnew ArrayList<String>.getClass() == new ArrayList<Integer>.getClass()
स्वयं पर ध्यान दें -

9
@ जॉब नहीं यह नहीं है। और C ++, metaprogramming नाम के टेम्प्लेट के लिए एक पूरी तरह से अलग दृष्टिकोण का उपयोग करता है। यह बतख-टाइपिंग का उपयोग करता है और आप उपयोगी चीजें कर सकते हैं जैसे template<T> T add(T v1, T v2) { return v1->add(v2); }और वहां आपके पास एक फ़ंक्शन बनाने का वास्तव में सामान्य तरीका है कि addदो चीज़ों से कोई फर्क नहीं पड़ता कि वे क्या चीजें हैं, उनके पास बस add()एक पैरामीटर के साथ नामित विधि है ।
त्रिनिदाद

7
@ जॉब यह कोड, वास्तव में उत्पन्न होता है। टेम्प्लेट सी प्रीप्रोसेसर को बदलने के लिए होते हैं और ऐसा करने के लिए उन्हें कुछ बहुत ही चालाक चालें बनाने में सक्षम होना चाहिए और वे स्वयं ट्यूरिंग-पूर्ण भाषा हैं। जावा जेनरिक केवल प्रकार के सुरक्षित कंटेनरों के लिए चीनी हैं, और सी # जेनरिक बेहतर हैं लेकिन फिर भी एक गरीब आदमी का सी ++ टेम्पलेट है।
त्रिनिदाद

3
@Job AFAIK, जावा जेनेरिक प्रत्येक नए जेनेरिक प्रकार के लिए एक वर्ग उत्पन्न नहीं करते हैं, यह सिर्फ कोड टाइप करता है जो जेनेरिक विधियों का उपयोग करता है, इसलिए यह वास्तव में मेटा-प्रोग्रामिंग IMO नहीं है। C # टेम्प्लेट प्रत्येक जेनेरिक प्रकार के लिए नया कोड उत्पन्न करते हैं, अर्थात, यदि आप सूची का उपयोग करें <int> और सूची <double> का C # दुनिया में उनमें से प्रत्येक के लिए कोड उत्पन्न होगा। टेम्प्लेट की तुलना में, C # जेनरिक को अभी भी यह जानना आवश्यक है कि आप किस प्रकार से इसे फीड कर सकते हैं। आप Addमेरे द्वारा दिए गए सरल उदाहरण को लागू नहीं कर सकते हैं , पहले से जाने बिना कोई रास्ता नहीं है कि इसे किस वर्ग में लागू किया जा सकता है, जो कि एक खामी है।
त्रिनिदाद

26

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

जहां तक ​​जावा-इन-लैंग्वेज की बात है, तो जावा के जेनेरिक में कुछ भी गलत नहीं है। जैसा कि C # बनाम जावा जेनेरिक में वर्णित है , जावा का जेनेरिक भाषा स्तर 1 पर बहुत ठीक है ।

क्या सब-अफीम है कि जेवीएम सीधे जेनेरिक का समर्थन नहीं करता है, जिसके कई प्रमुख परिणाम हैं:

  • क्लास लाइब्रेरी का प्रतिबिंब-संबंधित भाग पूर्ण प्रकार की जानकारी को उजागर नहीं कर सकता है जो जावा-इन-लैंग्वेज में उपलब्ध थी
  • कुछ प्रदर्शन जुर्माना शामिल है

मुझे लगता है कि मैं जो भेद कर रहा हूं वह एक पांडित्य है क्योंकि जावा-द-लैंग्वेज को सर्वसम्मति से जेवीएम के साथ लक्ष्य और जावा क्लास लाइब्रेरी को कोर लाइब्रेरी के रूप में संकलित किया गया है।

1 वाइल्डकार्ड के संभावित अपवाद के साथ, जो माना जाता है कि सामान्य मामले में प्रकार का अनुमान अनिर्णायक है। यह C # और जावा जेनरिक के बीच एक बड़ा अंतर है जिसका उल्लेख अक्सर नहीं किया जाता है। धन्यवाद, सुरमा।


14
जेवीएम को जेनरिक का समर्थन करने की आवश्यकता नहीं है। यह कहने के समान होगा कि C ++ में कोई टेम्पलेट नहीं है क्योंकि ix86 वास्तविक मशीन टेम्पलेट्स का समर्थन नहीं करता है, जो कि गलत है। समस्या यह है कि दृष्टिकोण सामान्य प्रकार को लागू करने के लिए संकलक लेता है। और फिर, C ++ टेम्प्लेट में रन-टाइम पर कोई जुर्माना शामिल नहीं है, कोई भी प्रतिबिंब नहीं है, मुझे लगता है कि केवल यही कारण है कि जावा में किए जाने की आवश्यकता होगी केवल भाषा डिजाइन खराब है।
त्रिनिदाद

2
@ ट्रिनिडाड लेकिन मैंने यह नहीं कहा कि जावा में जेनेरिक नहीं है, इसलिए मैं नहीं देखता कि यह समान कैसे है। और हाँ, JVM को उनका समर्थन करने की आवश्यकता नहीं है, ठीक उसी तरह जैसे C ++ को कोड को ऑप्टिमाइज़ करने की आवश्यकता नहीं है । लेकिन इसका अनुकूलन नहीं करना निश्चित रूप से "कुछ गलत" के रूप में गिना जाएगा।
रोमन स्टार्कोव

3
मैंने जो तर्क दिया है कि जेवीएम को उन पर प्रतिबिंब का उपयोग करने में सक्षम होने के लिए जेनरिक के लिए प्रत्यक्ष समर्थन की आवश्यकता नहीं है। दूसरों ने ऐसा किया है इसलिए यह किया जा सकता है, समस्या यह है कि जावा जेनेरिक से नई कक्षाएं उत्पन्न नहीं करता है, कोई पुनरीक्षण चीज नहीं है।
त्रिनिदाद

4
@ ट्रिनिडाड: सी ++ में, पर्याप्त समय और मेमोरी दिए गए कंपाइलर को मानते हुए, टेम्प्लेट का उपयोग कुछ भी करने के लिए किया जा सकता है और सब कुछ जो जानकारी के साथ किया जा सकता है वह संकलन समय पर उपलब्ध है (क्योंकि टेम्प्लेट का संकलन ट्यूरिंग-पूर्ण है), लेकिन वहाँ प्रोग्राम चलाने से पहले उपलब्ध जानकारी का उपयोग करके टेम्पलेट बनाने का कोई तरीका नहीं। इसके विपरीत, नेट में, एक प्रोग्राम के लिए इनपुट के आधार पर प्रकार बनाना संभव है; एक कार्यक्रम लिखना काफी आसान होगा जहां विभिन्न प्रकार की संख्या जो बनाई जा सकती है, ब्रह्मांड में इलेक्ट्रॉनों की संख्या से अधिक है।
सुपरकैट

2
@ ट्रिनिडाड: जाहिर है, कार्यक्रम का एक भी निष्पादन उन सभी प्रकारों (या, वास्तव में, उनमें से कुछ भी असीम अंश से परे) का निर्माण नहीं कर सकता है, लेकिन मुद्दा यह है कि ऐसा नहीं है। इसे केवल उन प्रकारों को बनाने की आवश्यकता है जो वास्तव में उपयोग किए जाते हैं। एक ऐसा कार्यक्रम हो सकता है जो किसी भी मनमानी संख्या (जैसे 8675309) को स्वीकार कर सकता है और इससे उस नंबर (जैसे Z8<Z6<Z7<Z5<Z3<Z0<Z9>>>>>>>) के लिए एक प्रकार अद्वितीय होता है, जिसमें किसी अन्य प्रकार से अलग सदस्य होंगे। C ++ में, किसी भी इनपुट से उत्पन्न होने वाले सभी प्रकारों का संकलन समय पर किया जाना होगा।
सुपरकैट

13

सामान्य आलोचना में संशोधन की कमी है। यह कहना है कि रनटाइम पर ऑब्जेक्ट्स में उनके सामान्य तर्कों के बारे में जानकारी नहीं होती है (हालाँकि यह जानकारी अभी भी फ़ील्ड्स, मेथड, कंस्ट्रक्टर्स और एक्सटेंडेड क्लास और इंटरफेस पर मौजूद है)। इसका मतलब है आप डाली सकता है, कहते हैं, ArrayList<String>करने के लिए List<File>। संकलक आपको चेतावनी देगा, लेकिन यह आपको चेतावनी भी देगा यदि आप ArrayList<String>इसे एक Objectसंदर्भ में असाइन करते हैं और फिर इसे डालते हैं List<String>। अपसाइड यह है कि जेनेरिक के साथ आप शायद कास्टिंग नहीं कर रहे हैं, अनावश्यक डेटा के बिना प्रदर्शन बेहतर है, और निश्चित रूप से, पिछड़े संगतता।

कुछ लोगों की शिकायत है कि आप सामान्य तर्कों ( void fn(Set<String>)और void fn(Set<File>)) के आधार पर ओवरलोड नहीं कर सकते । आपको इसके बजाय बेहतर विधि नामों का उपयोग करने की आवश्यकता है। ध्यान दें कि इस ओवरलोडिंग के लिए संशोधन की आवश्यकता नहीं होगी, क्योंकि ओवरलोडिंग एक स्थिर, संकलन-समय समस्या है।

आदिम प्रकार जेनरिक के साथ काम नहीं करते हैं।

वाइल्डकार्ड और सीमाएं काफी जटिल हैं। वे बहुत उपयोगी हैं। यदि जावा अपरिवर्तनीयता को प्राथमिकता देता है और बताए गए इंटरफेस को नहीं बताता है, तो उपयोग-साइड जेनरिक के बजाय घोषणा-पक्ष अधिक उपयुक्त होगा।


"ध्यान दें कि इस ओवरलोडिंग के लिए संशोधन की आवश्यकता नहीं होगी, क्योंकि ओवरलोडिंग एक स्थिर, संकलन-समय का मुद्दा है।" यह नहीं होगा? बिना रिवीजन के यह कैसे चलेगा? क्या JVM को रनटाइम में पता नहीं होगा कि आपके पास किस प्रकार का सेट है, ताकि यह पता चल सके कि किस विधि से कॉल करना है?
MatrixFrog

2
@ मेट्रोग्रॉग नं। जैसा कि मैं कहता हूं, ओवरलोडिंग एक संकलन-समय का मुद्दा है। कंपाइलर एक विशेष अधिभार का चयन करने के लिए स्थिर प्रकार की अभिव्यक्ति का उपयोग करता है, जो तब क्लास फ़ाइल के लिए लिखी गई विधि है। यदि आपके पास है तो Object cs = new char[] { 'H', 'i' }; System.out.println(cs);आप बकवास प्रिंट करवाएं। का प्रकार बदलें csकरने के लिए char[]और आप मिल जाएगा Hi
टॉम हॉल्टिन -

10

जावा जेनरिक चूसना क्योंकि आप निम्न कार्य नहीं कर सकते हैं:

public class AsyncAdapter<Parser,Adapter> extends AsyncTask<String,Integer,Adapter> {
    proptected Adapter doInBackground(String... keywords) {
      Parser p = new Parser(keywords[0]); // this is an error
      /* some more stuff I was hoping for but couldn't do because
         the compiler wouldn't let me
      */
    }
}

आपको उपरोक्त कोड में हर वर्ग को कसाई की जरूरत नहीं पड़ेगी ताकि वह काम कर सके क्योंकि अगर वास्तव में जेनेरिक क्लास के पैरामीटर थे तो यह होना चाहिए।


आपको हर वर्ग को कसाई की जरूरत नहीं है। आपको बस एक उपयुक्त कारखाना वर्ग जोड़ने और इसे AsyncAdapter में इंजेक्ट करने की आवश्यकता है। (और मौलिक रूप से यह जेनरिक के बारे में नहीं है, लेकिन यह तथ्य कि कंस्ट्रक्टर जावा में विरासत में नहीं हैं)।
पीटर टेलर

13
@PeterTaylor: एक उपयुक्त फैक्ट्री क्लास सभी बॉयलरप्लेट को दृष्टि में गड़बड़ी से बाहर निकाल देती है और यह अभी भी समस्या का समाधान नहीं करता है। अब हर बार जब मैं एक नई मिसाल कायम करना चाहता Parserहूँ तो मुझे कारखाने के कोड को और अधिक जटिल बनाने की आवश्यकता होगी। जबकि अगर "सामान्य" का मतलब वास्तव में सामान्य होता है, तो कारखानों और अन्य सभी बकवासों की आवश्यकता नहीं होगी जो डिजाइन पैटर्न के नाम से जाते हैं। यह कई कारणों में से एक है जो मुझे गतिशील भाषाओं में काम करना पसंद है।
davidk01 19

2
कभी-कभी C ++ में भी ऐसा ही होता है, जहां आप एक कंस्ट्रक्टर का पता नहीं दे सकते हैं, जब टेम्पलेट + वर्चुअल्स का उपयोग करते हैं - तो आप इसके बजाय बस फ़ैक्टरी ट्रैक्टर का उपयोग कर सकते हैं।
मार्क के कोवान

जावा बेकार है क्योंकि आप एक विधि के नाम से पहले "प्रचारित" नहीं लिख सकते हैं? बेचारे। गतिशील भाषाओं से चिपके रहते हैं। और विशेष रूप से हास्केल से सावधान रहें।
fdreger

5
  • अनुपस्थित सामान्य पैरामीटर के लिए संकलक चेतावनी, जो बिना किसी उद्देश्य के सेवा करते हैं, भाषा को अनावश्यक रूप से क्रिया करते हैं, जैसे public String getName(Class<?> klazz){ return klazz.getName();}

  • सामान्य ज्ञान सरणियों के साथ अच्छा नहीं खेलता

  • खोया प्रकार की जानकारी प्रतिबिंब को डस्टिंग और डक्ट टेप की गड़बड़ी बनाती है।


जब मुझे HashMapइसके बजाय उपयोग करने के लिए चेतावनी मिलती है तो मुझे गुस्सा आता है HashMap<String, String>
माइकल के

1
@Michael लेकिन यह है कि वास्तव में चाहिए जेनरिक साथ प्रयोग किया जा ...
वैकल्पिक

सरणी समस्या पुन: परिवर्तन के लिए जाती है। स्पष्टीकरण के लिए जावा जेनरिक पुस्तक (मगरमच्छ) देखें।
ncmathsadist

5

मुझे लगता है कि अन्य उत्तरों ने यह कुछ हद तक कहा है, लेकिन बहुत स्पष्ट रूप से नहीं। जेनरिक के साथ समस्याओं में से एक प्रतिबिंब प्रक्रिया के दौरान सामान्य प्रकार खो रहा है। उदाहरण के लिए:

List<String> arr = new ArrayList<String>();
assertTrue( ArrayList.class, arr.getClass() );
TypeVarible[] types = arr.getClass().getTypedVariables();

दुर्भाग्य से, लौटे प्रकार आपको यह नहीं बता सकते हैं कि गिरफ्तारी के सामान्य प्रकार स्ट्रिंग हैं। यह एक सूक्ष्म अंतर है, लेकिन यह महत्वपूर्ण है। चूँकि गिरफ्तारी रनटाइम के दौरान की जाती है, इसलिए जेनेरिक प्रकार को रनटाइम पर मिटा दिया जाता है ताकि आप इसका पता न लगा सकें। जैसा कि कुछ कहा गया ArrayList<Integer>है, ArrayList<String>प्रतिबिंब के दृष्टिकोण से समान है ।

यह जेनरिक के उपयोगकर्ता के लिए कोई मायने नहीं रख सकता है, लेकिन मान लीजिए कि हम कुछ फैंसी फ्रेमवर्क बनाना चाहते थे, जो कि फैंसी चीजों का पता लगाने के लिए प्रतिबिंब का उपयोग करते थे कि उपयोगकर्ता किस तरह से एक ठोस सामान्य प्रकार की घोषणा करता है।

Factory<MySpecialObject> factory = new Factory<MySpecialObject>();
MySpecialObject obj = factory.create();

मान लीजिए कि हम एक जेनेरिक फैक्ट्री चाहते हैं, MySpecialObjectक्योंकि हम इस उदाहरण के लिए घोषित ठोस जेनेरिक प्रकार का निर्माण कर रहे हैं। खैर फैक्ट्री वर्ग इस उदाहरण के लिए घोषित ठोस प्रकार का पता लगाने के लिए खुद से पूछताछ नहीं कर सकता क्योंकि जावा ने उन्हें मिटा दिया था।

.Net के जेनरिक में आप ऐसा कर सकते हैं क्योंकि रनटाइम के दौरान वस्तु जानती है कि यह सामान्य प्रकार है क्योंकि कंपाइलर ने इसे बाइनरी में कंप्लीट किया है। Erasures के साथ Java ऐसा नहीं कर सकता।


1

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

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

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

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

दूसरी ओर (मुझे वहां थोड़ा घाव हो गया और कुछ संतुलन प्रदान करने की आवश्यकता महसूस हुई), जब यह सरल संग्रह और नक्शे का उपयोग करता है, तो लिखा जाता है, लिखा जाता है और जब लिखा जाता है, तो यह अच्छा होता है और यह आमतौर पर बहुत कुछ नहीं जोड़ता है कोड के लिए जटिलता ( यदि कोई किसी और संग्रह या नक्शे लिखते हैं) । और जावा इस पर C # से बेहतर है। ऐसा लगता है कि सी # संग्रह में से कोई भी मैं कभी भी हैंडल जेनरिक का उपयोग नहीं करना चाहता। (मैं मानता हूं कि मेरे पास संग्रह में अजीब स्वाद है।)


अच्छा शेख़ी। हालाँकि, कई पुस्तकालय / रूपरेखाएँ मौजूद हैं, जो जेनरिक के बिना मौजूद हो सकती हैं, जैसे, गुइसे, गन्स, हाइबरनेट। और जेनेरिक उतने कठिन नहीं हैं कि एक बार आपकी आदत पड़ जाए। टाइप तर्क को टाइप करना और पढ़ना एक वास्तविक PITA है; यदि आप उपयोग कर सकते हैं तो यहां वैल मदद करता है।
माआर्टिनस

1
@maaartinus: कुछ समय पहले लिखा था। साथ ही ओपी ने "मुद्दों" के लिए कहा। मुझे जेनेरिक बेहद उपयोगी लगते हैं और उन्हें बहुत पसंद है - तब की तुलना में बहुत अधिक। हालाँकि, यदि आप अपना स्वयं का संग्रह लिख रहे हैं , तो उन्हें सीखना बहुत कठिन है। और उनकी उपयोगिता तब फीकी पड़ जाती है जब आपके संग्रह का प्रकार रन टाइम पर निर्धारित किया जाता है - जब संग्रह में एक विधि होती है जो आपको इसकी प्रविष्टियों का वर्ग बताती है । इस बिंदु पर जेनेरिक काम नहीं करते हैं और आपका आईडीई सैकड़ों अर्थहीन चेतावनियों का उत्पादन करेगा। जावा प्रोग्रामिंग का 99.99% इसमें शामिल नहीं है। लेकिन मैं चाहता हूँ सिर्फ एक था बहुत इस के साथ परेशानी का जब मैंने ऊपर लिखा था।
राल्फचिनपिन

जावा और सी # डेवलपर दोनों होने के नाते, मैं सोच रहा हूं कि आप जावा संग्रह क्यों पसंद करेंगे?
इविएलो स्लाव

Fine. But without generics this error would throw a ClassCastException really quick at run time with little time wasted.यह वास्तव में प्रोग्राम स्टार्टअप के बीच रन रन टाइम कितना होता है और प्रश्न में कोड की लाइन को हिट करने पर निर्भर करता है। यदि कोई उपयोगकर्ता त्रुटि की रिपोर्ट करता है और इसे पुन: पेश करने में आपको कई मिनट (या, सबसे खराब स्थिति, घंटे या दिन) लगते हैं, तो संकलन-समय सत्यापन बेहतर और बेहतर दिखने लगता है ...
मेसन व्हीलर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.