अमूर्त क्षेत्र क्यों नहीं?


100

क्यों जावा कक्षाओं में सार क्षेत्र नहीं हो सकते हैं जैसे कि वे सार तरीके हो सकते हैं?

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

मैं सिर्फ शुरुआत करने के लिए क्षेत्र को अमूर्त क्यों नहीं बना सका? क्या जावा को इसकी अनुमति देने के लिए डिज़ाइन किया गया है?


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

5
एक सुपर क्लास में फ़ील्ड्स को अमूर्त बनाने से, आपके पास विशिष्ट है कि हर सब क्लास में यह फ़ील्ड होना चाहिए, इसलिए यह एक गैर-अमूर्त क्षेत्र के लिए अलग नहीं है।
पीटर लॉरी

@ गैपर, मुझे यकीन नहीं है कि मैं आपकी बात का पालन कर रहा हूं। यदि सार वर्ग में एक गैर-सार स्थिर निर्दिष्ट किया गया था, तो यह मूल्य सभी उपवर्गों के माध्यम से भी स्थिर है। यदि यह अमूर्त था, तो इसके मूल्य को प्रत्येक उपवर्ग द्वारा लागू / आपूर्ति करना होगा। इसलिए, यह बिल्कुल समान नहीं होगा।
liltitus27

1
@ liltitus27 मुझे लगता है कि मेरी बात 3.5 साल पहले थी कि अमूर्त क्षेत्र को लागू करने से एक इंटरफ़ेस के उपयोगकर्ता को अलग करने के पूरे विचार को छोड़कर बहुत कुछ नहीं बदलेगा।
पीटर लॉरी

यह मददगार होगा क्योंकि यह बच्चे की कक्षा में कस्टम फील्ड एनोटेशन की अनुमति दे सकता है
geg

जवाबों:


103

आप अपने अमूर्त वर्ग में एक अंतिम फ़ील्ड के द्वारा वर्णित किया जा सकता है, जो इसके निर्माता (प्रारंभिक कोड) में आरंभिक है:

abstract class Base {

    final String errMsg;

    Base(String msg) {
        errMsg = msg;
    }

    abstract String doSomething();
}

class Sub extends Base {

    Sub() {
        super("Sub message");
    }

    String doSomething() {

        return errMsg + " from something";
    }
}

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


यहाँ इसे आज़माने के लिए एक संपादक नहीं मिला, लेकिन यही मैं पोस्ट करने जा रहा था ..
Tim

6
"संकलक एक चेतावनी देगा"। दरअसल, चाइल्ड कंस्ट्रक्टर एक गैर-मौजूद नॉरग कंस्ट्रक्टर का उपयोग करने की कोशिश कर रहा होगा और यह एक संकलन त्रुटि है (चेतावनी नहीं)।
स्टीफन C

और @ स्टेफेन सी की टिप्पणी में, "जैसे कि जब एक अमूर्त विधि लागू नहीं होती है" भी एक त्रुटि है, चेतावनी नहीं।
लॉरेंस गोंसाल्वेस

4
अच्छा जवाब - यह मेरे लिए काम किया। मुझे पता है कि इसे पोस्ट किए हुए कुछ समय हो गया है लेकिन लगता Baseहै कि इसे घोषित करने की आवश्यकता है abstract
स्टीव चैंबर्स

2
मैं अभी भी इस दृष्टिकोण को नापसंद करता हूं (भले ही यह जाने का एकमात्र तरीका है) क्योंकि यह एक निर्माणकर्ता के लिए एक अतिरिक्त तर्क जोड़ता है। नेटवर्क प्रोग्रामिंग में, मैं संदेश प्रकार बनाना पसंद करता हूं, जहां प्रत्येक नया संदेश एक अमूर्त प्रकार को लागू करता है, लेकिन एक अद्वितीय निर्दिष्ट चरित्र होना चाहिए जैसे 'ए' के ​​लिए एक्सेप्टमैसेज और 'एल' के लिए लॉगिनमेसेज। ये सुनिश्चित करते हैं कि एक कस्टम संदेश प्रोटोकॉल इस विशिष्ट चरित्र को तार पर रख सकेगा। ये वर्ग के लिए निहित हैं, इसलिए उन्हें फ़ील्ड के रूप में सबसे अच्छा काम किया जाएगा, न कि कुछ निर्माणकर्ता में पारित किया गया।
एडम ह्यूजेस

7

मुझे इसमें कोई मतलब नहीं दिख रहा है। आप फ़ंक्शन को अमूर्त वर्ग में ले जा सकते हैं और बस कुछ संरक्षित क्षेत्र को ओवरराइड कर सकते हैं। मुझे नहीं पता कि यह स्थिरांक के साथ काम करता है लेकिन प्रभाव समान है:

public abstract class Abstract {
    protected String errorMsg = "";

    public String getErrMsg() {
        return this.errorMsg;
    }
}

public class Foo extends Abstract {
    public Foo() {
       this.errorMsg = "Foo";
    }

}

public class Bar extends Abstract {
    public Bar() {
       this.errorMsg = "Bar";
    }
}

तो आपका कहना यह है कि क्या आप errorMsgउपवर्गों में कार्यान्वयन / ओवरराइडिंग / जो कुछ भी लागू करना चाहते हैं ? मुझे लगा कि आप बस बेस क्लास में विधि रखना चाहते थे और यह नहीं जानते थे कि फिर फील्ड से कैसे निपटना है।


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

1
वैसे इस तरह से हर उपवर्ग को मूल्य को ओवरराइड नहीं करना है। मैं बस यह भी परिभाषित कर सकता हूं protected String errorMsg;कि किसी भी तरह से लागू होता है कि मैं उपवर्गों में मूल्य निर्धारित करता हूं। यह उन अमूर्त वर्गों के समान है जो वास्तव में प्लेसहोल्डर के रूप में सभी तरीकों को लागू करते हैं ताकि डेवलपर्स को हर पद्धति को लागू न करना पड़े, हालांकि उन्हें इसकी आवश्यकता नहीं है।
फेलिक्स क्लिंग

7
कह रही है protected String errorMsg;करता नहीं लागू आप उपवर्गों में मूल्य सेट करें।
लारेंस गोंसाल्वेस

1
यदि मूल्य शून्य है यदि आप इसे "प्रवर्तन" के रूप में सेट नहीं करते हैं, तो आप गैर-प्रवर्तन क्या कहेंगे?
लारेंस गोंसाल्वेस

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

3

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


@ गौर - मेरे लिए यह स्पष्ट नहीं है कि अमूर्त क्षेत्रों को शामिल किया जा सकता था। कुछ इस तरह की प्रणाली पर और भाषा प्रयोज्य पर गहरा और मौलिक प्रभाव पड़ने की संभावना है। (एक ही तरह से कई वंशानुक्रम में गहरी समस्याएँ आती हैं ... जो कि अवांछित C ++ प्रोग्रामर को काट सकती हैं।)
स्टीफन C

0

आपके शीर्षक को पढ़कर, मुझे लगा कि आप अमूर्त उदाहरण सदस्यों का उल्लेख कर रहे हैं; और मैं उनके लिए बहुत उपयोग नहीं देख सकता था। लेकिन सार स्थिर सदस्य पूरी तरह से एक और मामला है।

मैंने अक्सर चाहा है कि मैं जावा में निम्नलिखित की तरह एक विधि की घोषणा कर सकता हूं:

public abstract class MyClass {

    public static abstract MyClass createInstance();

    // more stuff...

}

मूल रूप से, मैं इस बात पर जोर देना चाहूंगा कि मेरे मूल वर्ग के ठोस कार्यान्वयन एक विशिष्ट हस्ताक्षर के साथ एक स्थिर कारखाना विधि प्रदान करते हैं। यह मुझे एक ठोस वर्ग के साथ एक संदर्भ प्राप्त करने की अनुमति देगा Class.forName()और निश्चित होगा कि मैं अपने चयन के एक सम्मेलन में एक निर्माण कर सकता हूं।


1
हाँ ठीक है ... इसका मतलब है कि आप बहुत दूर तक प्रतिबिंब का उपयोग कर रहे हैं !!
स्टीफन C

मेरे लिए एक अजीब प्रोग्रामिंग आदत की तरह लगता है। Class.forName (..) एक डिजाइन दोष की तरह बदबू आ रही है।
व्हिस्की सियार

इन दिनों हम मूल रूप से हमेशा स्प्रिंग डीआई का उपयोग इस प्रकार की चीज़ को पूरा करने के लिए करते हैं; लेकिन इससे पहले कि फ्रेमवर्क ज्ञात और लोकप्रिय हो गया, हम गुण या XML फ़ाइलों में कार्यान्वयन कक्षाएं निर्दिष्ट करेंगे, फिर उन्हें लोड करने के लिए Class.forName () का उपयोग करें।
ड्रू विल्स

मैं आपको उनके लिए उपयोग का मामला दे सकता हूं। "अमूर्त क्षेत्रों" की अनुपस्थिति लगभग एक सामान्य सामान्य वर्ग में एक सामान्य प्रकार के क्षेत्र को घोषित करने में असंभव है @autowired T fieldName:। यदि Tइसे कुछ के रूप में परिभाषित किया गया है T extends AbstractController, तो टाइप AbstractControllerइरेज़र फ़ील्ड को प्रकार के रूप में उत्पन्न करने का कारण बनता है और जिसे स्वत: प्राप्त नहीं किया जा सकता क्योंकि यह ठोस नहीं है और अद्वितीय नहीं है। मैं आमतौर पर सहमत हूं कि उनके लिए बहुत उपयोग नहीं है, और इस तरह वे भाषा में नहीं हैं। मैं इससे सहमत नहीं हूं कि उनके लिए कोई उपयोग नहीं है।
डाब्लिक

0

एक अन्य विकल्प आधार क्षेत्र में सार्वजनिक (अंतिम, यदि आपको पसंद है) के रूप में क्षेत्र को परिभाषित करना है, और फिर आधार वर्ग के निर्माण में उस क्षेत्र को प्रारंभ करना है, जिसके आधार पर वर्तमान में उपवर्ग का उपयोग किया जा रहा है। यह थोड़ा छायादार है, इसमें यह एक परिपत्र निर्भरता का परिचय देता है। लेकिन, कम से कम यह एक निर्भरता नहीं है जो कभी भी बदल सकती है - यानी, उपवर्ग या तो मौजूद होगा या नहीं होगा, लेकिन उपवर्ग के तरीके या क्षेत्र के मूल्य को प्रभावित नहीं कर सकते हैं field

public abstract class Base {
  public final int field;
  public Base() {
    if (this instanceof SubClassOne) {
      field = 1;
    } else if (this instanceof SubClassTwo) {
      field = 2;
    } else {
      // assertion, thrown exception, set to -1, whatever you want to do 
      // to trigger an error
      field = -1;
    }
  }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.