2008-08-07 12 views
33

तो, जावा में, आपके कन्स्ट्रक्टर की पहली पंक्ति सुपर को कॉल करने के लिए है ... इसे स्पष्ट रूप से सुपर(), या स्पष्ट रूप से अन्य कन्स्ट्रक्टर को कॉल करना। मैं क्या जानना चाहता हूं, मैं इसके चारों ओर एक कोशिश क्यों नहीं कर सकता?मैं अपने सुपर() कॉल के आसपास एक कोशिश ब्लॉक का उपयोग क्यों नहीं कर सकता?

मेरा विशिष्ट मामला यह है कि मेरे पास एक परीक्षण के लिए नकली कक्षा है। कोई डिफ़ॉल्ट कन्स्ट्रक्टर नहीं है, लेकिन मैं चाहता हूं कि कोई परीक्षण को पढ़ने के लिए आसान बनाये। मैं कन्स्ट्रक्टर से एक रनटाइम अपवाद में फेंकने वाले अपवादों को भी लपेटना चाहता हूं।

तो, मुझे क्या करना चाहते हैं प्रभावी रूप से यह है:

public class MyClassMock extends MyClass { 
    public MyClassMock() { 
     try { 
      super(0); 
     } catch (Exception e) { 
      throw new RuntimeException(e); 
     } 
    } 

    // Mocked methods 
} 

लेकिन जावा शिकायत है कि सुपर पहले बयान नहीं है।

मेरे वैकल्पिक हल:

public class MyClassMock extends MyClass { 
    public static MyClassMock construct() { 
     try { 
      return new MyClassMock(); 
     } catch (Exception e) { 
      throw new RuntimeException(e); 
     } 
    } 

    public MyClassMock() throws Exception { 
     super(0); 
    } 

    // Mocked methods 
} 

यह सबसे अच्छा समाधान का है? जावा ने मुझे पूर्व क्यों नहीं किया?


को "क्यों" है कि जावा मुझे एक संभावित असंगत स्थिति में एक निर्माण वस्तु है यह बताने के लिए नहीं चाहता है ... फिर भी, एक नकली करने में, मुझे परवाह नहीं है के रूप में मेरा सबसे अच्छा अनुमान उसके बारे में। ऐसा लगता है कि मुझे उपर्युक्त करने में सक्षम होना चाहिए ... या कम से कम मुझे पता है कि उपरोक्त मेरे मामले के लिए सुरक्षित है ... या ऐसा लगता है कि यह वैसे भी होना चाहिए।

मैं परीक्षण कक्षा से उपयोग की जाने वाली किसी भी विधि को ओवरराइड कर रहा हूं, इसलिए कोई जोखिम नहीं है कि मैं अनियमित चर का उपयोग कर रहा हूं।

+1

एक दिलचस्प नोट यह है कि यह पूरी तरह जावा भाषा सीमा है। समकक्ष बाइटकोड पूरी तरह से मान्य है। – Antimony

+0

क्या आप वाकई बाइटकोड अभी भी वैध हैं? मुझे लगता है कि मैंने नीचे दिखाए गए परिणामी सुरक्षा छेद का शोषण करने के बाद इसे अवैध कर दिया है। – Joshua

+0

क्योंकि नियम इसे अनुमति नहीं देते हैं। [जेडीके स्पेक] पढ़ें (http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.10)। यहां तक ​​कि यदि आप कंपाइलर से पहले प्राप्त करते हैं तो सत्यापनकर्ता इसे अस्वीकार कर देगा। –

उत्तर

14

दुर्भाग्य से, कंपाइलर सैद्धांतिक सिद्धांतों पर काम नहीं कर सकते हैं, और भले ही आप जानते हों कि यह आपके मामले में सुरक्षित है, अगर उन्होंने इसे अनुमति दी है, तो इसे सभी मामलों के लिए सुरक्षित होना होगा।

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

सी # .NET में समान प्रावधान हैं, और एक निर्माता है कि एक आधार निर्माता कॉल की घोषणा करने के लिए एक ही रास्ता यह है:

public ClassName(...) : base(...) 
ऐसा करने में

, आधार निर्माता के शरीर से पहले बुलाया जाएगा निर्माता, और आप इस आदेश को नहीं बदल सकते हैं।

+1

क्यों न केवल आपको पकड़ ब्लॉक में इसका उपयोग करने से रोकता है? इसमें रैपिंग अपवादों के सामान्य मामले को शामिल किया गया है। – Antimony

2

मुझे नहीं पता कि जावा आंतरिक रूप से कैसे कार्यान्वित किया जाता है, लेकिन अगर सुपरक्लास के निर्माता एक अपवाद फेंकता है, तो आपके द्वारा विस्तारित कक्षा का एक उदाहरण नहीं है। toString() या equals() विधियों को कॉल करना असंभव होगा, उदाहरण के लिए, क्योंकि वे ज्यादातर मामलों में विरासत में हैं।

जावा कन्स्ट्रक्टर में सुपर() कॉल के चारों ओर कोशिश/पकड़ने की अनुमति दे सकता है यदि आप सुपरक्लास से सभी विधियों को ओवरराइड करते हैं, और 2. आप super.XXX() खंड का उपयोग नहीं करते हैं, लेकिन यह सब मुझे बहुत जटिल लगता है।

2

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

तो यह असीमित चर या किसी भी तरह का खतरा भी नहीं है। जब आप subclass 'creator से पहले बेस क्लास' कन्स्ट्रक्टर पर कुछ करने का प्रयास करते हैं, तो आप मूल रूप से कंपाइलर को मूल ऑब्जेक्ट इंस्टेंस का विस्तार करने के लिए कह रहे हैं जो अभी तक मौजूद नहीं है।

संपादित करें: आपके मामले में, MyClass आधार वस्तु बन जाता है, और MyClassMock एक उपवर्ग है।

5

यह किसी को अविश्वसनीय कोड से SecurityManager ऑब्जेक्ट बनाने से रोकने के लिए किया जाता है।

public class Evil : SecurityManager { 
    Evil() 
    { 
     try { 
     super(); 
     } catch { Throwable t } 
     { 
     } 
    } 
} 
6

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

मुझे ऑब्जेक्ट निर्माण विफल करने के उदाहरण के साथ शुरू करने दें।

के एक वर्ग के एक परिभाषित करते हैं, जैसे कि:

class A { 
    private String a = "A"; 

    public A() throws Exception { 
     throw new Exception(); 
    } 
} 

अब, चलो मान लेते हैं कि हम एक try...catch ब्लॉक में ग्रुप ए के एक वस्तु बनाने के लिए चाहते हैं।

A a = null; 
try{ 
    a = new A(); 
}catch(Exception e) { 
    //... 
} 
System.out.println(a); 

जाहिर है, इस कोड के उत्पादन में हो जाएगा: null

जावा A का आंशिक रूप से निर्मित संस्करण क्यों नहीं लौटाता है? आखिरकार, निर्माता द्वारा विफलता विफल हो जाती है, ऑब्जेक्ट का name फ़ील्ड पहले ही शुरू हो चुका है, है ना?

ठीक है, जावा A का आंशिक रूप से निर्मित संस्करण वापस नहीं कर सकता क्योंकि ऑब्जेक्ट सफलतापूर्वक बनाया नहीं गया था। वस्तु एक असंगत स्थिति में है, और इसलिए इसे जावा द्वारा त्याग दिया जाता है। आपका वैरिएबल ए भी शुरू नहीं हुआ है, इसे शून्य के रूप में रखा जाता है।

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

इस और अधिक व्यापक उदाहरण

class A { 
    private final int a; 
    public A() throws Exception { 
     a = 10; 
    } 
} 

class B extends A { 
    private final int b; 
    public B() throws Exception { 
     methodThatThrowsException(); 
     b = 20; 
    } 
} 

class C extends B { 
    public C() throws Exception { super(); } 
} 

जब C के निर्माता शुरू हो जाती है, जबकि B प्रारंभ करने में अपवाद तब होता है, तो पर देखो, क्या अंतिम int चर b का मूल्य हो सकता है?

इस तरह, ऑब्जेक्ट सी नहीं बनाया जा सकता है, यह फर्जी है, यह कचरा है, यह पूरी तरह शुरू नहीं हुआ है।

मेरे लिए, यह बताता है कि आपका कोड अवैध क्यों है।

-1

इसके आसपास जाने का एक तरीका एक निजी स्थैतिक कार्य को बुलाकर है। फिर कोशिश करने के लिए समारोह शरीर में कोशिश की जा सकती है।

public class Test { 
    public Test() { 
    this(Test.getObjectThatMightThrowException()); 
    } 
    public Test(Object o) { 
    //... 
    } 
    private static final Object getObjectThatMightThrowException() { 
    try { 
     return new ObjectThatMightThrowAnException(); 
    } catch(RuntimeException rtx) { 
     throw new RuntimeException("It threw an exception!!!", rtx); 
    } 
    } 
} 
+0

विस्तार करने के लिए देखभाल क्यों? – Unheilig

+0

मैंने थोड़ा सा विस्तार किया। बस ए? – aliteralmind

+0

क्यों एक निजी स्थिर कार्य कॉलिंग यहां काम करता है? और ओपी द्वारा कोड के साथ क्या गलत है जो आपको लगता है कि काम नहीं करता है? – Unheilig

0

मैं जानता हूँ कि इस सवाल का जवाब कई है, लेकिन मैं यह क्यों नहीं करने दिया जाएगा पर मेरे छोटे से tidbit देना चाहते हैं, विशेष रूप से जवाब देने के लिए क्यों जावा आपको ऐसा करने की अनुमति नहीं है। तो यहाँ तुम जाओ ...

अब, यह super() एक उपवर्ग के निर्माता में कुछ और पहले के नाम से जाना है को ध्यान में रखना है, तो, अगर आप अपने super() कॉल के आसपास try और catch ब्लॉकों का उपयोग किया था, ब्लॉक करने के लिए होगा इस तरह दिखेगा:

try { 
    super(); 
    ... 
} catch (Exception e) { 
    super(); //This line will throw the same error... 
    ... 
} 

तो सुपर() fails in the कोशिश block, it HAS to be executed first in the पकड़ block, so that सुपर runs before anything in your subclass रों निर्माता। यह आपको शुरुआत में उसी समस्या के साथ छोड़ देता है: यदि कोई अपवाद फेंक दिया जाता है, तो यह पकड़ा नहीं जाता है। (इस मामले में इसे पकड़ ब्लॉक में फिर से फेंक दिया जाता है।)

अब, उपरोक्त कोड जावा द्वारा किसी भी तरह से अनुमति नहीं है। यह कोड पहले सुपर कॉल का आधा निष्पादित कर सकता है, और उसके बाद इसे फिर से कॉल कर सकता है, जो कुछ सुपर क्लास के साथ कुछ समस्याएं पैदा कर सकता है।

अब, कारण जावा जाने नहीं है कि आप एक अपवाद बजाय फेंक बुला super() क्योंकि अपवाद कहीं और पकड़ा जा सकता है, और प्रोग्राम आपके उपवर्ग वस्तु पर super() बुला बिना जारी रहेगा, और है संभवतः क्योंकि अपवाद आपके ऑब्जेक्ट को पैरामीटर के रूप में ले सकता है और विरासत आवृत्ति चर के मान को बदलने का प्रयास करता है, जिसे अभी तक प्रारंभ नहीं किया गया है।

संबंधित मुद्दे

 संबंधित मुद्दे