Java Enum परिभाषा


151

मुझे लगा कि मैं जावा जेनरिक को अच्छी तरह समझता हूं, लेकिन फिर मैं java.lang.Enum में निम्नलिखित में से आया:

class Enum<E extends Enum<E>>

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



इस सवाल के बेहतर उत्तर हैं: stackoverflow.com/a/3068001/2413303
EpicPandaForce

जवाबों:


105

इसका अर्थ यह है कि एनम के लिए टाइप तर्क को एक एनम से प्राप्त करना होगा जो स्वयं उसी प्रकार का तर्क है। ये केसे हो सकता हे? प्रकार तर्क को नया प्रकार बनाकर। इसलिए अगर मुझे StatusCode नाम की एक एनम मिली है, तो यह इसके बराबर होगी:

public class StatusCode extends Enum<StatusCode>

अब अगर आप बाधाओं की जाँच करते हैं, तो हमें मिल गया है Enum<StatusCode>- इसलिए E=StatusCode। आइए जाँच करें: Eविस्तार करता है Enum<StatusCode>? हाँ! हम ठीक हैं।

उदाहरण के लिए, कि कहने के लिए सक्षम किया जा रहा - आप अच्छी तरह से अपने आप को पूछ क्या इस की बात :) खैर, है इसका मतलब है कि Enum के लिए एपीआई ही उल्लेख कर सकते हैं हो सकता है Enum<E>लागू करता Comparable<E>। बेस क्लास तुलना करने में सक्षम है (एनम के मामले में) लेकिन यह सुनिश्चित कर सकता है कि यह केवल एक दूसरे के साथ सही तरह के एनम की तुलना करता है। (संपादित करें: ठीक है, लगभग - तल पर संपादित देखें।)

मैंने अपने सी # प्रोटोकॉलबफर्स ​​के पोर्ट में कुछ इसी तरह का उपयोग किया है। "संदेश" (अपरिवर्तनीय) और "बिल्डर्स" हैं (परस्पर, संदेश बनाने के लिए उपयोग किया जाता है) - और वे प्रकार के जोड़े के रूप में आते हैं। इसमें शामिल इंटरफेस हैं:

public interface IBuilder<TMessage, TBuilder>
  where TMessage : IMessage<TMessage, TBuilder> 
  where TBuilder : IBuilder<TMessage, TBuilder>

public interface IMessage<TMessage, TBuilder>
  where TMessage : IMessage<TMessage, TBuilder> 
  where TBuilder : IBuilder<TMessage, TBuilder>

इसका मतलब है कि एक संदेश से आप एक उपयुक्त बिल्डर प्राप्त कर सकते हैं (जैसे संदेश की एक प्रति लेने के लिए और कुछ बिट्स को बदलने के लिए) और एक बिल्डर से आप एक उपयुक्त संदेश प्राप्त कर सकते हैं जब आपने इसका निर्माण किया हो। यह एपीआई का एक अच्छा काम है उपयोगकर्ताओं को वास्तव में इस बारे में परवाह करने की ज़रूरत नहीं है - यह बहुत ही जटिल है, और जहां यह है वहां जाने के लिए कई पुनरावृत्तियों को लिया।

संपादित करें: ध्यान दें कि यह आपको अजीब प्रकार बनाने से नहीं रोकता है जो एक प्रकार के तर्क का उपयोग करते हैं जो स्वयं ठीक है, लेकिन जो एक ही प्रकार नहीं है। इसका उद्देश्य आपको गलत मामले से बचाने के बजाय सही मामले में लाभ देना है ।

इसलिए यदि Enumजावा में वैसे भी "विशेष रूप से" संभाला नहीं गया था, तो आप (जैसा कि टिप्पणी में उल्लेख किया गया है) निम्नलिखित प्रकार बना सकते हैं:

public class First extends Enum<First> {}
public class Second extends Enum<First> {}

Secondके Comparable<First>बजाय लागू होगा Comparable<Second>... लेकिन Firstखुद ही ठीक हो जाएगा।


1
@ कार्ट्स: मुझे यह याद नहीं है कि बिल्डर और संदेश दोनों में यह सामान्य क्यों है। मुझे पूरा यकीन है कि मैं उस रास्ते से नीचे नहीं गया होता अगर मुझे इसकी आवश्यकता नहीं होती थी :)
जॉन स्कीट

1
@ सैय्यमहम्मद: हां, यह उस प्रकार के मिश्रण के पहलू को नहीं रोकता है । मैं इस बारे में एक नोट जोड़ूंगा।
जॉन स्कीट

1
"मैंने अपने सी # प्रोटोकॉलबफर्स ​​के पोर्ट में कुछ समान उपयोग किया है।" लेकिन यह अलग है क्योंकि बिल्डरों के पास ऐसे तरीके हैं जो टाइप पैरामीटर प्रकार को लौटाते हैं। Enumकोई उदाहरण विधियाँ नहीं है जो टाइप पैरामीटर प्रकार लौटाता है।
नवागत

1
@JonSkeet: यह देखते हुए कि एनम वर्ग हमेशा से ऑटोग्रॉन्ग हैं, मैं दावा कर रहा हूं कि class Enum<E>सभी मामलों में यह पर्याप्त है। यदि आप वास्तव में टाइप सुरक्षा सुनिश्चित करने के लिए आवश्यक हैं, तो जेनरिक में आपको केवल अधिक प्रतिबंधक बाउंड का उपयोग करना चाहिए।
newacct

1
@JonSkeet: इसके अलावा, अगर Enumउपवर्गों हमेशा ऑटोजनरेटेड नहीं थे, केवल कारण है कि आप की आवश्यकता होगी class Enum<E extends Enum<?>>से अधिक class Enum<E>का उपयोग करने की क्षमता है ordinalके लिए compareTo()। हालाँकि, यदि आप इसके बारे में सोचते हैं, तो यह भाषा के दृष्टिकोण से कोई मतलब नहीं है, जिससे आप अपने अध्यादेशों के माध्यम से दो अलग-अलग प्रकार के एनमों की तुलना कर सकें। इसलिए, Enum.compareTo()उस उपयोग का कार्यान्वयन ordinalकेवल Enumउपवर्गों के ऑटोजेनर होने के संदर्भ में समझ में आता है । यदि आप मैन्युअल रूप से उपवर्ग कर सकते हैं Enum, compareToतो शायद होना ही चाहिए abstract
newacct

27

निम्नलिखित पुस्तक जावा जेनरिक एंड कलेक्शंस से स्पष्टीकरण का एक संशोधित संस्करण है : हमारे पास एक Enumघोषित है

enum Season { WINTER, SPRING, SUMMER, FALL }

जिसे एक वर्ग में विस्तारित किया जाएगा

final class Season extends ...

...Enums के लिए किसी भी तरह-पैरामीटरित आधार वर्ग होना कहां है। आइए जानें कि क्या होना है। खैर, आवश्यकताओं में से Seasonएक यह है कि इसे लागू करना चाहिए Comparable<Season>। तो हम जरूरत के लिए जा रहे हैं

Season extends ... implements Comparable<Season>

आप इसके लिए क्या उपयोग कर सकते हैं ...यह काम करने की अनुमति देगा? यह देखते हुए कि इसका एक मानदंड बनना है Enum, एकमात्र विकल्प है Enum<Season>, ताकि आपके पास यह हो सके:

Season extends Enum<Season>
Enum<Season> implements Comparable<Season>

तो Enumजैसे प्रकार पर पैरामीटर किया गया है Season। से सार Seasonऔर आपको लगता है कि Enumकिसी भी प्रकार का पैरामीटर संतुष्ट करता है

 E extends Enum<E>

मौरिस नाफ्टलिन (सह-लेखक, जावा जेनरिक और कलेक्शंस)


1
@newacct ठीक है, मुझे अब यह मिल गया है: आप एनम <ई> के उदाहरण के लिए सभी एनमों को पसंद करेंगे, है ना? (क्योंकि यदि वे एक एनुम उपप्रकार के उदाहरण हैं, तो ऊपर दिया गया तर्क लागू होता है।) लेकिन तब आपके पास टाइप-सेफ एनम नहीं होगा, इसलिए एनाम होने की बात बिल्कुल भी नहीं खोएगी।
मौरिस नाफ्टालिन

1
@newacct क्या नहीं आप उस बात पर जोर देना चाहते हैं Seasonके औजार Comparable<Season>?
मौरिस नफ्तालिन

2
@newacct Enum की परिभाषा को देखें। एक उदाहरण को दूसरे के साथ तुलना करने के लिए उनके अध्यादेशों की तुलना करना पड़ता है। तो compareToविधि के तर्क को एक उपप्रकार के रूप में घोषित किया जाना चाहिए Enum, या संकलक (सही ढंग से) का कहना है कि इसका कोई नियम नहीं है।
मौरिस नाफ्टलिन

2
@MauriceNaftalin: यदि जावा ने मैन्युअल रूप से सबक्लासिंग को प्रतिबंधित नहीं किया है Enum, तो यह संभव है कि अभी class OneEnum extends Enum<AnotherEnum>{}भी कैसे Enumघोषित किया गया है। यह बहुत मतलब नहीं होगा तो फिर एक और के साथ enum का एक प्रकार की तुलना करने के सक्षम होने के लिए Enums ' compareToके रूप में वैसे भी घोषित मतलब नहीं होगा। सीमाएं इसके लिए कोई सहायता प्रदान नहीं करती हैं।
newacct

2
@MauriceNaftalin: यदि ऑर्डिनल कारण था, तो public class Enum<E extends Enum<?>>यह भी पर्याप्त होगा।
newacct

6

इसका उदाहरण एक सरल उदाहरण और एक तकनीक से दिया जा सकता है जिसका उपयोग उप-वर्गों के लिए जंजीर पद्धति कॉल को लागू करने के लिए किया जा सकता है। नीचे दिए गए एक उदाहरण में setNameएक Nodeबहुत अच्छी तरह से पीछा करने के लिए काम नहीं करेगा City:

class Node {
    String name;

    Node setName(String name) {
        this.name = name;
        return this;
    }
}

class City extends Node {
    int square;

    City setSquare(int square) {
        this.square = square;
        return this;
    }
}

public static void main(String[] args) {
    City city = new City()
        .setName("LA")
        .setSquare(100);    // won't compile, setName() returns Node
}

इसलिए हम एक सामान्य घोषणा में एक उप-वर्ग का संदर्भ दे सकते हैं, ताकि Cityअब सही प्रकार वापस आए:

abstract class Node<SELF extends Node<SELF>>{
    String name;

    SELF setName(String name) {
        this.name = name;
        return self();
    }

    protected abstract SELF self();
}

class City extends Node<City> {
    int square;

    City setSquare(int square) {
        this.square = square;
        return self();
    }

    @Override
    protected City self() {
        return this;
    }

    public static void main(String[] args) {
       City city = new City()
            .setName("LA")
            .setSquare(100);                 // ok!
    }
}

आपके समाधान में एक अनियंत्रित डाली गई है: return (CHILD) this;एक getThis () विधि जोड़ने पर विचार करें: protected CHILD getThis() { return this; } देखें: angelikalanger.com/GenericsFAQ/FAQSections/…
रोलैंड

एक लिंक के लिए @ रोलैंड धन्यवाद, मैंने इससे एक विचार उधार लिया है। क्या आप मुझे समझा सकते हैं या एक लेख को निर्देशित कर सकते हैं जो यह बता सकता है कि इस विशेष मामले में यह एक बुरा व्यवहार क्यों है? लिंक में विधि को अधिक टाइपिंग की आवश्यकता है और यह मुख्य तर्क है कि मैं इससे क्यों बच रहा हूं। मैंने इस मामले में कास्ट एरर कभी नहीं देखा है + मुझे पता है कि कुछ अपरिहार्य कास्ट एरर हैं - यानी जब एक ही कलेक्शन के लिए कई तरह के ऑब्जेक्ट्स स्टोर करते हैं। इसलिए अगर अनियंत्रित जातियां महत्वपूर्ण नहीं हैं और डिजाइन कुछ जटिल है ( Node<T>मामला नहीं है), तो मैं समय बचाने के लिए उनकी उपेक्षा कर रहा हूं।
एंड्री चेशेव

आपका संपादन ऐसा नहीं है, जो कुछ संश्लिष्ट चीनी को जोड़ने से पहले अलग है, पर विचार करें कि निम्न कोड वास्तव में संकलन करेगा लेकिन एक रन टाइम एरर फेंक देगा: `नोड <सिटी> नोड = नया नोड <सिटी> () .सेटनेम (" नोड ")। setSquare (1); `अगर आप जावा बाइट कोड को देखते हैं तो आप देखेंगे कि टाइप इरेज़र के कारण स्टेटमेंट return (SELF) this;संकलित किया जाता है return this;, इसलिए आप इसे छोड़ सकते हैं।
रोलैंड

@ रोलैंड थैंक्स, यह वही है जिसकी मुझे ज़रूरत थी - जब मैं मुक्त होऊंगा तो उदाहरण को अपडेट करूंगा।
एंड्रे चशचेव

निम्नलिखित लिंक भी अच्छा है: angelikalanger.com/GenericsFAQ/FAQSections/…
रोलैंड

3

आप केवल एक ही आश्चर्यचकित नहीं हैं कि इसका क्या मतलब है; अराजक जावा ब्लॉग देखें ।

"यदि एक वर्ग इस वर्ग का विस्तार करता है, तो उसे एक पैरामीटर ई पास करना चाहिए। पैरामीटर ई की सीमा उस वर्ग के लिए है जो इस कक्षा को उसी पैरामीटर ई के साथ बढ़ाता है"।


1

इस पोस्ट ने मुझे 'पुनरावर्ती सामान्य प्रकार' की इन समस्याओं को पूरी तरह से स्पष्ट कर दिया है। मैं सिर्फ एक और मामला जोड़ना चाहता था जहां यह विशेष संरचना आवश्यक है।

मान लीजिए कि आपके जेनेरिक ग्राफ में जेनेरिक नोड्स हैं:

public abstract class Node<T extends Node<T>>
{
    public void addNeighbor(T);

    public void addNeighbors(Collection<? extends T> nodes);

    public Collection<T> getNeighbor();
}

तब आपके पास विशेष प्रकार के रेखांकन हो सकते हैं:

public class City extends Node<City>
{
    public void addNeighbor(City){...}

    public void addNeighbors(Collection<? extends City> nodes){...}

    public Collection<City> getNeighbor(){...}
}

यह अभी भी मुझे एक class Foo extends Node<City>ऐसा शहर बनाने की अनुमति देता है जहां फू शहर से असंबंधित है।
newacct

1
यकीन है, और यह गलत है? मुझे ऐसा नहीं लगता। नोड <सिटी> द्वारा प्रदान किया गया आधार अनुबंध अभी भी सम्मानित है, केवल आपके फू सबक्लास कम उपयोगी हैं जब से आप फोस के साथ काम करना शुरू करते हैं, लेकिन शहरों को एडीटी से बाहर निकालते हैं। इसके लिए एक उपयोग का मामला हो सकता है, लेकिन सबसे अधिक संभावना सरल और अधिक उपयोगी है बस सामान्य पैरामीटर को उपवर्ग के रूप में बनाने के लिए। लेकिन किसी भी तरह से, डिजाइनर के पास वह विकल्प है।
mdma

@ मम्मा: मैं सहमत हूं। तो फिर बाउंड ओवर का क्या उपयोग होता है class Node<T>?
newacct

1
@nozebacle: आपका उदाहरण प्रदर्शित नहीं करता है कि "यह विशेष संरचना आवश्यक है"। class Node<T>अपने उदाहरण के साथ पूरी तरह से संगत है।
newacct

1

यदि आप Enumस्रोत कोड को देखते हैं, तो इसके निम्नलिखित हैं:

public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {

    public final int compareTo(E o) {
        Enum<?> other = (Enum<?>)o;
        Enum<E> self = this;
        if (self.getClass() != other.getClass() && // optimization
            self.getDeclaringClass() != other.getDeclaringClass())
            throw new ClassCastException();
        return self.ordinal - other.ordinal;
    }

    @SuppressWarnings("unchecked")
    public final Class<E> getDeclaringClass() {
        Class<?> clazz = getClass();
        Class<?> zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
    }

    public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                                String name) {
        T result = enumType.enumConstantDirectory().get(name);
        if (result != null)
            return result;
        if (name == null)
            throw new NullPointerException("Name is null");
        throw new IllegalArgumentException(
            "No enum constant " + enumType.getCanonicalName() + "." + name);
    } 
}

पहली बात पहले, क्या E extends Enum<E>मतलब है? इसका मतलब है कि टाइप पैरामीटर एक ऐसी चीज है जो एनम से फैली हुई है, और कच्चे प्रकार के साथ पैरामीट्रिक नहीं है (यह स्वयं द्वारा पैरामीरिज्ड है)।

यदि आपके पास एनम है तो यह प्रासंगिक है

public enum MyEnum {
    THING1,
    THING2;
}

जो, अगर मुझे सही से पता है, तो इसका अनुवाद किया जाता है

public final class MyEnum extends Enum<MyEnum> {
    public static final MyEnum THING1 = new MyEnum();
    public static final MyEnum THING2 = new MyEnum();
}

तो इसका मतलब यह है कि MyEnum निम्नलिखित तरीके प्राप्त करता है:

public final int compareTo(MyEnum o) {
    Enum<?> other = (Enum<?>)o;
    Enum<MyEnum> self = this;
    if (self.getClass() != other.getClass() && // optimization
        self.getDeclaringClass() != other.getDeclaringClass())
        throw new ClassCastException();
    return self.ordinal - other.ordinal;
}

और इससे भी महत्वपूर्ण बात,

    @SuppressWarnings("unchecked")
    public final Class<MyEnum> getDeclaringClass() {
        Class<?> clazz = getClass();
        Class<?> zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? (Class<MyEnum>)clazz : (Class<MyEnum>)zuper;
    }

यह getDeclaringClass()उचित Class<T>ऑब्जेक्ट को कास्ट करता है ।

एक तरह से स्पष्ट उदाहरण वह है जो मैंने इस प्रश्न पर दिया था कि आप इस निर्माण से बच नहीं सकते हैं यदि आप एक सामान्य सीमा को निर्दिष्ट करना चाहते हैं।


कुछ भी नहीं है आप में दिखाया गया है compareToया getDeclaringClassआवश्यकता है extends Enum<E>बाध्य।
newacct

0

विकिपीडिया के अनुसार, इस पैटर्न को क्यूरेटिकली आवर्ती टेम्पलेट पैटर्न कहा जाता है । मूल रूप से, CRTP पैटर्न का उपयोग करके, हम आसानी से टाइप कास्टिंग के बिना उपवर्ग प्रकार को संदर्भित कर सकते हैं, जिसका अर्थ है कि पैटर्न का उपयोग करके, हम आभासी फ़ंक्शन की नकल कर सकते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.