Java enum शाब्दिक में सामान्य प्रकार के पैरामीटर क्यों नहीं होने चाहिए?


148

जावा एनम महान हैं। तो जेनरिक हैं। निश्चित रूप से हम सभी प्रकार के क्षरण के कारण बाद की सीमाओं को जानते हैं। लेकिन एक बात है जो मुझे समझ नहीं आ रही है, मैं इस तरह से एक एनम क्यों नहीं बना सकता:

public enum MyEnum<T> {
    LITERAL1<String>,
    LITERAL2<Integer>,
    LITERAL3<Object>;
}

<T>बदले में यह सामान्य प्रकार का पैरामीटर तब विभिन्न स्थानों में उपयोगी हो सकता है। एक विधि के लिए एक सामान्य प्रकार के पैरामीटर की कल्पना करें:

public <T> T getValue(MyEnum<T> param);

या यहां तक ​​कि एनम वर्ग में ही:

public T convert(Object o);

अधिक ठोस उदाहरण # 1

चूँकि उपर्युक्त उदाहरण कुछ के लिए बहुत सारगर्भित लग सकता है, यहाँ एक और वास्तविक जीवन का उदाहरण है कि मैं ऐसा क्यों करना चाहता हूँ। इस उदाहरण में मैं उपयोग करना चाहता हूं

  • Enums, क्योंकि तब मैं संपत्ति कुंजी का एक सीमित सेट की गणना कर सकता हूं
  • जेनरिक, क्योंकि तब मेरे पास स्टोरेज प्रॉपर्टीज के लिए मेथड-लेवल टाइप-सेफ्टी हो सकती है
public interface MyProperties {
     public <T> void put(MyEnum<T> key, T value);
     public <T> T get(MyEnum<T> key);
}

अधिक ठोस उदाहरण # 2

मेरे पास डेटा प्रकारों की गणना है:

public interface DataType<T> {}

public enum SQLDataType<T> implements DataType<T> {
    TINYINT<Byte>,
    SMALLINT<Short>,
    INT<Integer>,
    BIGINT<Long>,
    CLOB<String>,
    VARCHAR<String>,
    ...
}

प्रत्येक एनुम शाब्दिक में स्पष्ट रूप से सामान्य प्रकार के आधार पर अतिरिक्त गुण होंगे <T> , जबकि एक ही समय में, एक enum (अपरिवर्तनीय, सिंगलटन, enumerable, आदि) होने के नाते।

सवाल:

क्या किसी ने ऐसा नहीं सोचा? क्या यह एक कंपाइलर-संबंधित सीमा है? इस तथ्य को ध्यान में रखते हुए, कि कीवर्ड " एनम " को सिंटैक्टिक शुगर के रूप में लागू किया गया है, जेवीएम को उत्पन्न कोड का प्रतिनिधित्व करता है, मुझे यह सीमा समझ में नहीं आती है।

यह मुझे कौन समझा सकता है? जवाब देने से पहले, इस पर विचार करें:

  • मुझे पता है जेनेरिक प्रकार मिट जाते हैं :-)
  • मुझे पता है कि क्लास ऑब्जेक्ट्स का उपयोग करके वर्कअराउंड हैं। वे कामगार हैं।
  • सामान्य प्रकार के कंपाइलर-जनरेट किए गए प्रकार के परिणाम जहां भी लागू होते हैं (उदाहरण के लिए कॉल करते समय)
  • जेनेरिक टाइप <T> एनम पर होगा। इसलिए यह एनम के प्रत्येक शाब्दिक अर्थ से बंधा है। इसलिए कंपाइलर को पता होता है कि कुछ लिखते समय किस प्रकार का आवेदन करना हैString string = LITERAL1.convert(myObject); Integer integer = LITERAL2.convert(myObject);
  • T getvalue()विधि में सामान्य प्रकार के पैरामीटर के लिए भी यही लागू होता है । कॉल करने पर कंपाइलर टाइप कास्टिंग लागू कर सकता हैString string = someClass.getValue(LITERAL1)

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

5
(+1) मैं अपनी परियोजना में एक प्रकार की सुरक्षा खाई को बंद करने की कोशिश के बीच में हूं, इस मनमानी सीमा से मर चुका हूं। बस इस पर विचार करें: enumजावा 1.5 से पहले हमारे द्वारा उपयोग किए जाने वाले "टाइपफेस एनम" मुहावरे में बदल जाएं। अचानक, आप अपने एनम सदस्यों को मानकीकृत कर सकते हैं। शायद यही मैं अब करने जा रहा हूं।
मार्को टोपोलनिक

1
@EdwinDalorzo: से एक ठोस उदाहरण के साथ सवाल अपडेट किया गया jOOQ , जहां इस अतीत में बेहद उपयोगी हो गया होता।
लुकास ईडर

2
@LukasEder मैं अब आपकी बात देखता हूं। एक शांत एक नई सुविधा की तरह लग रहा है। हो सकता है कि आपको प्रोजेक्ट सिक्का मेलिंग सूची में यह सुझाव देना चाहिए कि मैं वहां अन्य दिलचस्प प्रस्ताव देखता हूं, लेकिन आपका जैसा कोई नहीं।
एडविन डेलोरोज़ो

1
पूर्णतया सहमत। जेनरिक के बिना एनम अपंग हैं। आपका केस # 1 भी मेरा है। अगर मुझे जेनेरिक एनम चाहिए तो मैं JDK5 छोड़ दूंगा और इसे सादे पुराने जावा 1.4 स्टाइल में लागू करूंगा। इस दृष्टिकोण का अतिरिक्त लाभ भी है : मुझे एक ही कक्षा या पैकेज में सभी स्थिरांक रखने के लिए मजबूर नहीं किया जाता है। इस प्रकार, पैकेज-बाय-फीचर शैली को बहुत बेहतर ढंग से पूरा किया जाता है। यह केवल कॉन्फ़िगरेशन-जैसे "एनम" के लिए एकदम सही है - स्थिरांक उनके तार्किक अर्थ के अनुसार संकुल में फैले हुए हैं (और अगर मैं उन सभी को देखना चाहता हूं, तो मुझे टाइप हियरार्की दिखाया गया है)।
टामस ज़ालूस 21

जवाबों:


50

इस पर अब JEP-301 एन्हांस्ड एनम के रूप में चर्चा की जा रही है । जेईपी में दिया गया उदाहरण है, जो ठीक वही है जो मैं देख रहा था:

enum Argument<X> { // declares generic enum
   STRING<String>(String.class), 
   INTEGER<Integer>(Integer.class), ... ;

   Class<X> clazz;

   Argument(Class<X> clazz) { this.clazz = clazz; }

   Class<X> getClazz() { return clazz; }
}

Class<String> cs = Argument.STRING.getClazz(); //uses sharper typing of enum constant

दुर्भाग्य से, JEP अभी भी महत्वपूर्ण मुद्दों से जूझ रहा है: http://mail.openjdk.java.net/pipermail/amber-spec-experts/2017-May/000041.html


2
दिसंबर 2018 तक, जेईपी 301 के आसपास फिर से जीवन के कुछ संकेत थे , लेकिन उस चर्चा को छोड़ देने से यह स्पष्ट होता है कि समस्याएं अभी भी हल नहीं हुई हैं।
पोंट

11

जवाब सवाल में है:

प्रकार के क्षरण के कारण

इन दो विधियों में से कोई भी संभव नहीं है, क्योंकि तर्क प्रकार मिट जाता है।

public <T> T getValue(MyEnum<T> param);
public T convert(Object);

उन तरीकों को महसूस करने के लिए जो आप अपने एनम का निर्माण कर सकते हैं:

public enum MyEnum {
    LITERAL1(String.class),
    LITERAL2(Integer.class),
    LITERAL3(Object.class);

    private Class<?> clazz;

    private MyEnum(Class<?> clazz) {
      this.clazz = clazz;
    }

    ...

}

2
खैर, संकलन-समय पर क्षरण होता है। लेकिन कंपाइलर टाइप-चेक के लिए सामान्य प्रकार की जानकारी का उपयोग कर सकता है। और फिर जाति टाइप करने के लिए सामान्य प्रकार को "कनवर्ट करता है"। मैं इस सवाल पर फिर से विचार करूंगा
लुकास एडर

2
यकीन नहीं होता कि मैं पूरी तरह से फॉलो करता हूं। ले लो public T convert(Object);। मैं अनुमान लगा रहा हूं कि यह विधि विभिन्न प्रकारों के एक समूह को संकीर्ण कर सकती है <T>, जो उदाहरण के लिए एक स्ट्रिंग है। स्ट्रिंग ऑब्जेक्ट का निर्माण रनटाइम है - कंपाइलर कुछ भी नहीं करता है। इसलिए आपको रनटाइम प्रकार, जैसे कि String.class को जानना होगा। या क्या मैं कुछ न कुछ भूल रहा हूं?
मार्टिन एलेस्टेन

6
मुझे लगता है कि आप इस तथ्य को याद कर रहे हैं कि जेनेरिक प्रकार <T> एनम (या इसके उत्पन्न वर्ग) पर है। एनुम का एकमात्र उदाहरण इसके शाब्दिक हैं, जो सभी एक निरंतर सामान्य प्रकार के बंधन प्रदान करते हैं। इसलिए सार्वजनिक टी कन्वर्ट (ऑब्जेक्ट) में टी के बारे में कोई अस्पष्टता नहीं है;
लुकास एडर

2
अहा! समझ गया। तुम मुझसे ज्यादा चतुर हो :)
मार्टिन एलेस्टेन

1
मुझे लगता है कि यह सब कुछ इसी तरह से मूल्यांकन किया जा रहा है एक enum के लिए वर्ग के लिए नीचे आता है। जावा में, हालांकि चीजें स्थिर लगती हैं , वे नहीं हैं। public final static String FOO;एक स्थिर ब्लॉक के साथ विचार करें static { FOO = "bar"; }- संकलन समय पर ज्ञात होने पर भी स्थिरांक का मूल्यांकन किया जाता है।
मार्टिन एलेस्टेन

5

क्योंकि तुम नहीं कर सकते। गंभीरता से। इसे भाषा की युक्ति में जोड़ा जा सकता है। यह नहीं किया गया है। यह कुछ जटिलता जोड़ देगा। लागत के लिए लाभ का मतलब है कि यह उच्च प्राथमिकता नहीं है।

अद्यतन: वर्तमान में JEP 301 के तहत भाषा में जोड़ा जा रहा है : एन्हांस्ड एनम


क्या आप जटिलताओं के बारे में विस्तार से बता सकते हैं?
Mr_and_Mrs_D

4
मैं अब कुछ दिनों के लिए इस बारे में सोच रहा हूं, और मुझे अभी भी कोई जटिलता नहीं दिख रही है।
मार्को टोपोलनिक

4

ENUM में अन्य विधियाँ हैं जो काम नहीं करेंगी। क्या होगाMyEnum.values() लौटेगा?

व्हाट अबाउट MyEnum.valueOf(String name) ?

मान के लिए यदि आप सोचते हैं कि संकलक जेनेरिक विधि की तरह बना सकता है

सार्वजनिक स्थिर MyEnum valueOf (स्ट्रिंग नाम);

इसे कॉल करने के लिए MyEnum<String> myStringEnum = MyEnum.value("some string property"), यह भी काम नहीं करेगा। उदाहरण के लिए यदि आप कॉल करते हैं तो क्या होगा MyEnum<Int> myIntEnum = MyEnum.<Int>value("some string property")? सही तरीके से काम करने के लिए उस तरीके को लागू करना संभव नहीं है, उदाहरण के लिए अपवाद को फेंकने या अशक्त करने के लिए जब आप इसे MyEnum.<Int>value("some double property")टाइप इरेज़र के कारण कहते हैं ।


2
वे काम क्यों नहीं करेंगे? वे बस वाइल्डकार्ड का उपयोग करेंगे ... MyEnum<?>[] values()औरMyEnum<?> valueOf(...)
लुकास एडर

1
लेकिन तब आप इस तरह असाइनमेंट नहीं कर सकते थे MyEnum<Int> blabla = valueOf("some double property");क्योंकि प्रकार संगत नहीं होते हैं। इसके अलावा आप उस स्थिति में अशक्त होना चाहते हैं क्योंकि आप MyEnum <Int> को वापस करना चाहते हैं, जो दोहरी संपत्ति के नाम के लिए मौजूद नहीं है और आप मिटाने के कारण उस विधि को ठीक से काम नहीं कर सकते हैं।
user1944408

यदि आप मानों () के माध्यम से लूप करते हैं, तो आपको MyEnum <?> का उपयोग करने की आवश्यकता होगी जो कि आप आमतौर पर नहीं चाहते हैं क्योंकि उदाहरण के लिए आप केवल अपने Int गुणों के माध्यम से लूप नहीं कर सकते। इसके अलावा आपको बहुत सी कास्टिंग करने की ज़रूरत है, जिसे आप टालना चाहते हैं, मैं आपको हर प्रकार के लिए अलग-अलग
एनम

खैर, मुझे लगता है कि आप दोनों नहीं हो सकते। आमतौर पर, मैं अपने "एनम" कार्यान्वयन का सहारा लेता हूं। यह सिर्फ इतना है कि Enumअन्य उपयोगी सुविधाओं का एक बहुत कुछ है, और यह एक वैकल्पिक और बहुत ही उपयोगी होगा ...
लुकास एडर

0

स्पष्ट रूप से यह किसी भी समस्या की तलाश में एक समाधान की तरह लगता है।

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

टेक्स्ट बुक एनम का एक उदाहरण लें। यह बहुत उपयोगी या सुसंगत नहीं है:

public enum Planet<T>{
    Earth<Planet>,
    Venus<String>,
    Mars<Long>
    ...etc.
}

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

इसके अतिरिक्त आप जटिल रूपांतरणों का प्रबंधन कैसे करेंगे?

उदाहरण के लिए

public enum BadIdea<T>{
   INSTANCE1<Long>,
   INSTANCE2<MyComplexClass>;
}

String Integerनाम या क्रम की आपूर्ति के साथ इसका आसान पर्याप्त है । लेकिन जेनरिक आपको किसी भी प्रकार की आपूर्ति करने की अनुमति देगा। आप रूपांतरण को कैसे प्रबंधित करेंगे MyComplexClass? अब आपकी कंपकंपी दो कंस्ट्रक्टरों को यह जानकर मजबूर कर देती है कि सीमित प्रकार के उप-समूह हैं जिन्हें जेनेरिक एनमों को आपूर्ति की जा सकती है और अवधारणा (जेनरिक) के लिए अतिरिक्त भ्रम का सामना करना पड़ सकता है जो पहले से ही बहुत सारे प्रोग्रामर को लगता है।


17
कुछ उदाहरणों के बारे में सोचना जहां उपयोगी नहीं होगा, एक भयानक तर्क है कि यह कभी उपयोगी नहीं होगा।
एलियास वासिलेन्को जुएल

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

1
@ nsfyn55 Enums जावा के सबसे जटिल, सबसे जादुई भाषा विशेषताओं में से एक हैं। उदाहरण के लिए, कोई अन्य भाषा सुविधा नहीं है, जो स्थैतिक विधियों को स्वतः उत्पन्न करती है। साथ ही, प्रत्येक एनुम प्रकार पहले से ही सामान्य प्रकार का एक उदाहरण है।
मार्को टोपोलनिक

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

1
मैंने आपके विरोधाभास के उल्लेख पर ध्यान नहीं दिया ... जावा इसका समर्थन करता है, केवल इसका उपयोग-साइट है, जो इसे थोड़ा अस्पष्ट बनाता है। interface Converter<IN, OUT> { OUT convert(IN in); } <E> Set<E> convertListToSet(List<E> in, Converter<? super List<E>, ? extends Set<E>> converter) { return converter.convert(in); }हमें हर बार मैन्युअल रूप से काम करना होता है कि किस प्रकार का उपभोग किया जाता है और किसका उत्पादन किया जाता है, और उसके अनुसार सीमाएँ निर्दिष्ट करें।
मार्को टोपोलनिक

-2

बेसम्यू "एनम" गणन के लिए संक्षिप्त नाम है। यह नामांकित स्थिरांक का एक सेट है जो कोड को बेहतर पठनीय बनाने के लिए क्रमिक संख्याओं के स्थान पर खड़ा है।

मैं यह नहीं देखता कि एक प्रकार-पैरामीटर वाले स्थिरांक का इच्छित अर्थ क्या हो सकता है।


1
में java.lang.String: public static final Comparator<String> CASE_INSENSITIVE_ORDER। आप अब देखना? :-)
लुकास एडर

4
आपने कहा कि आप यह नहीं देखते हैं कि एक प्रकार का पैरामीटर स्थिर का अर्थ क्या हो सकता है। इसलिए मैंने आपको एक टाइप-पैरामीटराइज्ड कॉन्स्टेंट (फंक्शन पॉइंटर नहीं) दिखाया।
लुकास ईडर

बेशक नहीं, क्योंकि जावा भाषा एक फ़ंक्शन पॉइंटर के रूप में ऐसी चीज नहीं जानती है। फिर भी, स्थिर नहीं प्रकार को मानकीकृत किया जाता है, लेकिन यह प्रकार है। और टाइप पैरामीटर स्वयं स्थिर है, टाइप वेरिएबल नहीं है।
इंगो

लेकिन एनुम सिंटेक्स सिर्फ सिंटैक्टिक शुगर है। नीचे, वे बिल्कुल वैसा ही हैं CASE_INSENSITIVE_ORDER... अगर Comparator<T>एक एनम थे, तो किसी भी संबंधित बाध्य के साथ शाब्दिक क्यों नहीं हैं <T>?
लुकास एडर

यदि तुलनित्र <T> एक एनम थे, तो वास्तव में हमारे पास सभी प्रकार की अजीब चीजें हो सकती हैं। शायद कुछ ऐसा ही है इंटेगर <टी>। लेकिन ऐसा नहीं है।
इंगो

-3

मुझे लगता है क्योंकि मूल रूप से एनमों को प्रेरित नहीं किया जा सकता है

यदि आप JVM ने आपको ऐसा करने की अनुमति दी है, तो आप टी क्लास कहाँ सेट करेंगे?

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

नई MyEnum <> ()?

फिर भी निम्नलिखित दृष्टिकोण उपयोगी हो सकता है

public enum MyEnum{

    LITERAL1("s"),
    LITERAL2("a"),
    LITERAL3(2);

    private Object o;

    private MyEnum(Object o) {
        this.o = o;
    }

    public Object getO() {
        return o;
    }

    public void setO(Object o) {
        this.o = o;
    }   
}

8
मुझे यकीन नहीं है कि मुझे यह setO()तरीका पसंद है enum। मैं enums स्थिरांक और मेरे लिए है कि अपरिवर्तनीय का अर्थ है पर विचार करें । इसलिए अगर यह संभव है, तो भी मैं ऐसा नहीं करूंगा।
मार्टिन एलेस्टेन

2
संकलक द्वारा उत्पन्न कोड में एनमों को अस्थिर किया जाता है। प्रत्येक शाब्दिक वास्तव में निजी कंस्ट्रक्टरों को बुलाकर बनाया गया है। इसलिए, कंपाइलर-टाइम पर कंस्ट्रक्टर को जेनेरिक टाइप पास करने का मौका मिलेगा।
लुकास एडर

2
एनम पर सेट पूरी तरह से सामान्य हैं। खासकर अगर आप एनम का उपयोग एक एकल बनाने के लिए कर रहे हैं (जोशुआ बलोच द्वारा आइटम 3 प्रभावी जावा)
प्रेस्टन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.