जावा थ्रेडेबल के सामान्य उपवर्गों की अनुमति क्यों नहीं देता है?


146

जावा भाषा सिपाहीकरण के अनुसार , तीसरा संस्करण:

यदि सामान्य वर्ग प्रत्यक्ष या अप्रत्यक्ष उपवर्ग है तो यह एक संकलन-समय की त्रुटि है Throwable

मैं यह समझना चाहता हूं कि यह निर्णय क्यों किया गया है। सामान्य अपवादों में क्या गलत है?

(जहाँ तक मुझे पता है, जेनेरिक बस संकलित समय सिंटैक्टिक चीनी हैं, और वे Objectवैसे भी .classफाइलों में अनुवादित होंगे , इसलिए प्रभावी रूप से एक जेनेरिक क्लास की घोषणा कर रहा है जैसे कि इसमें सब कुछ एक था Object। कृपया मुझे सही करें अगर मैं गलत हूं। ।)


1
सामान्य प्रकार के तर्कों को ऊपरी बाउंड द्वारा प्रतिस्थापित किया जाता है, जो डिफ़ॉल्ट रूप से ऑब्जेक्ट है। अगर आपके पास List <जैसा कुछ है? ए> फैली हुई है, तो ए का उपयोग क्लास फाइलों में किया जाता है।
टॉर्स्टन मारेक

साभार @Torsten मैंने पहले उस मामले के बारे में नहीं सोचा था।
होसम एलवाई

2
यह एक अच्छा साक्षात्कार प्रश्न है, यह एक।
स्केफमैन

@TorstenMarek: यदि कोई कॉल करता है myList.get(i), तो स्पष्ट रूप से getअभी भी एक रिटर्न देता है Object। संकलक Aकुछ समय में बाधा को पकड़ने के लिए एक डाली सम्मिलित करता है? यदि नहीं, तो ओपी सही है कि अंत में यह Objectक्रम में एस पर उबलता है। (वर्ग फ़ाइल में मेटाडेटा के बारे में निश्चित रूप से शामिल है A, लेकिन यह केवल मेटाडेटा AFAIK है।)
मिहाई दानिला

जवाबों:


155

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

try {
   doSomeStuff();
} catch (SomeException<Integer> e) {
   // ignore that
} catch (SomeException<String> e) {
   crashAndBurn()
}

दोनों SomeException<Integer>और SomeException<String>एक ही प्रकार से मिट जाते हैं, जेवीएम के पास अपवाद उदाहरणों को अलग करने का कोई तरीका नहीं है, और इसलिए यह बताने का कोई तरीका नहीं है कि किस catchब्लॉक को निष्पादित किया जाना चाहिए।


3
लेकिन "पुन: प्रयोज्य" का क्या अर्थ है?
aberrant80

61
तो नियम "सामान्य प्रकार थ्रोसेबल को उप-वर्ग नहीं कर सकता है" नहीं होना चाहिए, बल्कि इसके बजाय "कैच क्लॉस को हमेशा कच्चे प्रकार का उपयोग करना चाहिए"।
आर्ची

3
वे केवल एक ही प्रकार के साथ दो कैच ब्लॉक का उपयोग कर अस्वीकार कर सकते हैं। इसलिए कि SomeExc <Integer> का उपयोग करना केवल कानूनी होगा, केवल SomeExc <Integer> और SomeExc <String> का एक साथ उपयोग करना अवैध होगा। यह कोई समस्या नहीं है, या यह होगा?
विलिअम बुआर

3
ओह, अब मैं उसे समझ गया। मेरे समाधान से RuntimeException की समस्याएँ होंगी, जिन्हें घोषित नहीं किया जाना चाहिए। इसलिए अगर SomeExc RuntimeException का एक उपवर्ग है, तो मैं SomeExc <Integer> को फेंक और स्पष्ट रूप से पकड़ सकता था, लेकिन शायद कुछ अन्य फ़ंक्शन चुपचाप SomeExc <String> को फेंक रहा है और SomeExc <Integer> के लिए मेरा कैच ब्लॉक गलती से भी पकड़ लेगा।
विलिअम बुउर

4
@ SuperJedi224 - नहीं, यह उन्हें सही करता है - यह देखते हुए कि जेनेरिक को पीछे की संगत होना पड़ता है।
स्टीफन सी

14

यहाँ अपवाद का उपयोग करने का एक सरल उदाहरण है:

class IntegerExceptionTest {
  public static void main(String[] args) {
    try {
      throw new IntegerException(42);
    } catch (IntegerException e) {
      assert e.getValue() == 42;
    }
  }
}

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

इसके विपरीत, एक नए अपवाद की निम्न परिभाषा निषिद्ध है, क्योंकि यह एक पैरामीटर प्रकार बनाता है:

class ParametricException<T> extends Exception {  // compile-time error
  private final T value;
  public ParametricException(T value) { this.value = value; }
  public T getValue() { return value; }
}

उपरोक्त रिपोर्ट को एक त्रुटि संकलन का प्रयास:

% javac ParametricException.java
ParametricException.java:1: a generic class may not extend
java.lang.Throwable
class ParametricException<T> extends Exception {  // compile-time error
                                     ^
1 error

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

class ParametricExceptionTest {
  public static void main(String[] args) {
    try {
      throw new ParametricException<Integer>(42);
    } catch (ParametricException<Integer> e) {  // compile-time error
      assert e.getValue()==42;
    }
  }
}

यह अनुमति नहीं है, क्योंकि कैच क्लॉज में प्रकार पुन: प्रयोज्य नहीं है। इस लेखन के समय, सन कंपाइलर ऐसे मामले में वाक्य रचना त्रुटियों की एक झरना रिपोर्ट करता है:

% javac ParametricExceptionTest.java
ParametricExceptionTest.java:5: <identifier> expected
    } catch (ParametricException<Integer> e) {
                                ^
ParametricExceptionTest.java:8: ')' expected
  }
  ^
ParametricExceptionTest.java:9: '}' expected
}
 ^
3 errors

क्योंकि अपवाद पैरामीट्रिक नहीं हो सकते हैं, वाक्यविन्यास प्रतिबंधित है ताकि टाइप को एक पहचानकर्ता के रूप में लिखा जाए, जिसमें कोई निम्न पैरामीटर न हो।


2
जब आप if रेफ़िबल ’कहते हैं, तो आपका क्या मतलब है? not रिइपेबल ’एक शब्द नहीं है।
ForYourOwnGood

1
मुझे स्वयं इस शब्द का पता नहीं था, लेकिन Google में एक त्वरित खोज मुझे यह मिली: java.sun.com/docs/books/jls/third_edition/html/…
ऐली


13

यह अनिवार्य रूप से है क्योंकि यह खराब तरीके से डिजाइन किया गया था।

यह समस्या स्वच्छ अमूर्त डिज़ाइन को रोकती है जैसे,

public interface Repository<ID, E extends Entity<ID>> {

    E getById(ID id) throws EntityNotFoundException<E, ID>;
}

तथ्य यह है कि एक पकड़ क्लॉज जेनेरिक के लिए असफल हो जाएगा पुन: संशोधित नहीं किया जाता है उसके लिए कोई बहाना नहीं है। कंपाइलर ठोस जेनेरिक प्रकारों को अस्वीकार कर सकता है जो थ्रोबेबल का विस्तार करते हैं और कैच क्लॉस के अंदर जेनरिक को हटाते हैं।


+1। मेरा जवाब - stackoverflow.com/questions/30759692/…
ZhongYu

1
जिस तरह से वे इसे बेहतर तरीके से डिजाइन कर सकते थे, वह ~ 10 साल के ग्राहकों के कोड को असंगत बताकर किया गया था। यह एक व्यवहार्य व्यावसायिक निर्णय था। डिजाइन सही था ... संदर्भ दिया
स्टीफन सी

1
तो आप इस अपवाद को कैसे पकड़ेंगे? काम करने का एकमात्र तरीका कच्चे प्रकार को पकड़ना है EntityNotFoundException। लेकिन यह जेनेरिक को बेकार कर देगा।
Frans

4

प्रकार-शुद्धता के लिए संकलन-समय पर जेनरिक जाँच की जाती है। जेनेरिक प्रकार की जानकारी तब टाइप एरासुरे नामक प्रक्रिया में हटा दी जाती है । उदाहरण के लिए, List<Integer>गैर-जेनेरिक प्रकार में परिवर्तित हो जाएगा List

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

चलिए मान लेते हैं कि आपको Throwableइस तरह का विस्तार करने की अनुमति है :

public class GenericException<T> extends Throwable

अब निम्नलिखित कोड पर विचार करें:

try {
    throw new GenericException<Integer>();
}
catch(GenericException<Integer> e) {
    System.err.println("Integer");
}
catch(GenericException<String> e) {
    System.err.println("String");
}

प्रकार के क्षरण के कारण , रनटाइम को पता नहीं चलेगा कि किस ब्लॉक को निष्पादित करना है।

इसलिए यह एक संकलित समय त्रुटि है यदि एक सामान्य वर्ग थ्रोबेब का प्रत्यक्ष या अप्रत्यक्ष उपवर्ग है।

स्रोत: प्रकार के क्षरण के साथ समस्याएं



नहीं यह नहीं। टॉरस्टेन के जवाब ने मेरी मदद नहीं की, क्योंकि यह नहीं समझाता कि किस प्रकार का क्षरण / संशोधन है।
गुड नाइट नर्ड प्राइड

2

मुझे उम्मीद है कि यह इसलिए है क्योंकि वहाँ कोई रास्ता नहीं है के लिए मानकीकरण की गारंटी है। निम्नलिखित कोड पर विचार करें:

try
{
    doSomethingThatCanThrow();
}
catch (MyException<Foo> e)
{
    // handle it
}

जैसा कि आप नोट करते हैं, पैरामीटराइजेशन सिर्फ सिंटैक्टिक शुगर है। हालाँकि, कंपाइलर यह सुनिश्चित करने की कोशिश करता है कि संकलन के दायरे में किसी ऑब्जेक्ट के सभी संदर्भों में पैरामीटराइजेशन बना रहता है। अपवाद के मामले में, कंपाइलर के पास यह गारंटी देने का कोई तरीका नहीं है कि MyException को केवल उस स्कोप से फेंका गया है जो वह प्रसंस्करण है।


हां, लेकिन इसे "असुरक्षित" के रूप में क्यों नहीं चिह्नित किया गया है, उदाहरण के लिए जातियों के साथ?
इलाजेन्सो

क्योंकि एक कलाकार के साथ, आप संकलक से कह रहे हैं "मुझे पता है कि यह निष्पादन पथ अपेक्षित परिणाम देता है।" एक अपवाद के साथ, आप यह नहीं कह सकते (सभी संभावित अपवादों के लिए) "मुझे पता है कि यह कहाँ फेंका गया था।" लेकिन, जैसा कि मैं ऊपर कहता हूं, यह एक अनुमान है; मैं वहां नहीं था।
kdgregory

"मुझे पता है कि यह निष्पादन पथ अपेक्षित परिणाम देता है।" तुम्हें पता नहीं है, तुम आशा करते हो। इसीलिए जेनेरिक और डाउनकास्ट सांख्यिकीय रूप से असुरक्षित हैं, लेकिन फिर भी उन्हें अनुमति नहीं है। मैंने टॉरस्टेन के उत्तर को उकेरा, क्योंकि वहां मुझे समस्या दिखाई देती है। यहाँ मैं नहीं।
20

यदि आप नहीं जानते कि एक वस्तु एक विशेष प्रकार की है, तो आपको इसे नहीं डालना चाहिए। एक कलाकार का पूरा विचार यह है कि आपके पास संकलक से अधिक ज्ञान है, और उस ज्ञान को स्पष्ट रूप से कोड का हिस्सा बना रहे हैं।
22

हां, और यहां आपको कंपाइलर से अधिक ज्ञान हो सकता है, क्योंकि आप MyException से MyException <Foo> में अनियंत्रित रूपांतरण करना चाहते हैं। शायद आप "जानते हैं" यह एक MyException <Foo> होगा।
क्लेन्ज़ो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.