2016-01-02 5 views
13

यह प्रश्न this StackOverflow question about unsafe casts: Java Casting method without knowing what to cast to द्वारा शामिल किया गया था।टाइप एरर के बाद फसल का सामान्य रिटर्न वैल्यू कब होता है?

  • सम्मिलित प्रकार यदि डाले: सवाल मैं इस व्यवहार को मैं विशुद्ध रूप से विनिर्देश के आधार पर व्याख्या नहीं कर सकता का सामना करना पड़ा का जवाब दे जबकि

    मैं ओरेकल डॉक्स पर जावा ट्यूटोरियल में निम्नलिखित बयान पाया प्रकार की सुरक्षा को संरक्षित करने के लिए आवश्यक है। The Java Tutorials: Type Erasure

यह नहीं समझाया गया है क्या "यदि आवश्यक हो तो" वास्तव में इसका मतलब है, और मैं इन डाले बिल्कुल Java Language Specification में के बारे में कोई जिक्र नहीं पाया है, इसलिए मैं प्रयोग करने के लिए शुरू कर दिया।

// Java source 
public static <T> T identity(T x) { 
    return x; 
} 
public static void main(String args[]) { 
    String a = identity("foo"); 
    System.out.println(a.getClass().getName()); 
    // Prints 'java.lang.String' 

    Object b = identity("foo"); 
    System.out.println(b.getClass().getName()); 
    // Prints 'java.lang.String' 
} 

javac साथ संकलित और the Java Decompiler साथ decompiled:

के कोड का निम्न भाग पर नजर डालते हैं

// Decompiled code 
public static void main(String[] paramArrayOfString) 
{ 
    // The compiler inserted a cast to String to ensure type safety 
    String str = (String)identity("foo"); 
    System.out.println(str.getClass().getName()); 

    // The compiler omitted the cast, as it is not needed 
    // in terms of runtime type safety, but it actually could 
    // do an additional check. Is it some kind of optimization 
    // to decrease overhead? Where is this behaviour specified? 
    Object localObject1 = identity("foo"); 
    System.out.println(localObject1.getClass().getName()); 
} 

मैं देख सकता हूँ एक डाली जो पहले में सुरक्षा टाइप सुनिश्चित करता है कि वहाँ मामला, लेकिन दूसरे मामले में इसे छोड़ा गया है। यह बिल्कुल ठीक है, क्योंकि मैं Object टाइप किए गए चर में वापसी मान को संग्रहीत करना चाहता हूं, इसलिए कास्ट प्रकार सुरक्षा के अनुसार कड़ाई से जरूरी नहीं है। हालांकि यह असुरक्षित डाले के साथ एक दिलचस्प व्यवहार की ओर जाता है:

public class Erasure { 
    public static <T> T unsafeIdentity(Object x) { 
     return (T) x; 
    } 

    public static void main(String args[]) { 
     // I would expect c to be either an Integer after this 
     // call, or a ClassCastException to be thrown when the 
     // return value is not Integer 
     Object c = Erasure.<Integer>unsafeIdentity("foo"); 
     System.out.println(c.getClass().getName()); 
     // but Prints 'java.lang.String' 
    } 
} 

संकलित और decompiled, मैं कोई प्रकार डाली देख रनटाइम पर सही वापसी प्रकार सुनिश्चित करने के लिए:

// The type of the return value of unsafeIdentity is not checked, 
// just as in the second example. 
Object localObject2 = unsafeIdentity("foo"); 
System.out.println(localObject2.getClass().getName()); 

इसका मतलब यह है कि यदि एक सामान्य समारोह किसी दिए गए प्रकार का ऑब्जेक्ट लौटाएं, यह गारंटी नहीं है कि अंततः उस प्रकार को वापस कर देगा। उपरोक्त कोड का उपयोग कर एप्लिकेशन पहले बिंदु पर विफल हो जाएगा जहां यह को Integer पर वापसी मान डालने के लिए प्रयास करता है, तो मुझे लगता है कि यह fail-fast principle को तोड़ता है।

संकलन के दौरान इस कलाकार को डालने वाले कंपाइलर के सटीक नियम क्या हैं जो प्रकार की सुरक्षा सुनिश्चित करते हैं और ये नियम कहां निर्दिष्ट हैं?

संपादित करें:

मुझे लगता है कि संकलक कोड में खुदाई नहीं और साबित होता है कि सामान्य कोड वास्तव में देता है यह क्या करना चाहिए की कोशिश, लेकिन यह एक assertation डाल सकता है, या कम से कम एक प्रकार , कच्चा (जो यह पहले से ही के रूप में पहली उदाहरण में देखा, विशिष्ट मामलों में करता है) सही वापसी प्रकार सुनिश्चित करने के लिए तो बाद एक ClassCastException फेंक होगा:

// It could compile to this, throwing ClassCastException: 
Object localObject2 = (Integer)unsafeIdentity("foo"); 
+0

मैं इस विषय पर एक विशेषज्ञ नहीं हूं, लेकिन मुझे नहीं लगता कि संकलक इस मामले में कोई जांच कर सकता है क्योंकि (1) जब यह लाइन 'रिटर्न (टी) एक्स;' देखता है, तो इसका कोई रास्ता नहीं है सांख्यिकीय रूप से जानने के लिए कि 'x' को' टी' में परिवर्तित नहीं किया जा सकता है; और (2) जब आप वास्तव में 'unsafeIdentity' कहते हैं, तो संकलक यह नहीं जानता कि यह असफल हो जाएगा क्योंकि यह विधि के कोड में नहीं पहुंचाएगा और विफलताओं की तलाश करेगा। असल में, मुझे लगता है कि इसका मतलब है कि विधि में '(टी) 'कास्ट बेकार है। – ajb

+0

धन्यवाद @ajb, निश्चित रूप से (टी) को कास्ट करना बेकार है, यह वास्तव में एक न्यूनतम उदाहरण है। लेकिन यह आसानी से बाहरी कार्य को 'ऑब्जेक्ट ओ = (इंटीजर) असुरक्षितता ("foo") में संकलित कर सकता है;', और वह 'क्लासकास्ट अपवाद' फेंक देगा या क्या मुझे कुछ याद आ रहा है? –

+0

मुझे नहीं लगता कि संकलक कभी भी एक दावा सम्मिलित नहीं करेगा जब तक कि आप इसे कोड न करें, तो इसे यहां क्यों करना चाहिए? लेकिन शेष प्रश्न दिलचस्प है, +1। – 5gon12eder

उत्तर

4

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

इस मामले में, संकलक के मिट कोड इस तरह दिखता है:

public static Object identity(Object x) { 
    return x; 
} 
public static void main(String args[]) { 
    String a = (String)identity("foo"); 
    System.out.println(a.getClass().getName()); 

    Object b = identity("foo"); 
    System.out.println(b.getClass().getName()); 
} 

पहले मामले में, कलाकारों, मिट कोड में आवश्यक है, क्योंकि अगर आप इसे हटा दिया, मिट कोड संकलन नहीं होगा । ऐसा इसलिए है क्योंकि जावा गारंटी देता है कि पुन: प्रयोज्य प्रकार के संदर्भ चर में रनटाइम पर क्या होता है instanceOf होना चाहिए कि पुन: प्रयोज्य प्रकार, इसलिए रनटाइम चेक यहां आवश्यक है।

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

+1

बहुत बहुत धन्यवाद, यह स्पष्ट रूप से मेरे प्रश्न का उत्तर देता है।मुझे आशा थी कि किसी को उस स्पेक में कुछ प्रासंगिक मिल सकता है जिसे मैं नहीं कर सका, लेकिन ऐसा लगता है कि जावा ट्यूटोरियल लेख में टाइप एरर के बारे में एकमात्र उल्लेख है, जेएलएस 15.5 में उल्लेख करें (@HopefullyHelpful द्वारा पाया गया), और ओपनजेडीके स्रोत (@ मार्को 13 द्वारा पाया गया) –

-1

संस्करण 1 बेहतर है क्योंकि यह compiletime में विफल रहता है।

typesafe संस्करण 1 गैर विरासत कोड:

class Erasure { 
public static <T> T unsafeIdentity(T x) { 
    //no cast necessary, type checked in the parameters at compile time 
    return x; 
} 

public static void main(String args[]) { 
    // This will fail at compile time and you should use Integer c = ... in real code 
    Object c = Erasure.<Integer>unsafeIdentity("foo"); 
    System.out.println(c.getClass().getName()); 
    } 
} 

typesafe संस्करण 2 विरासत कोड (A run-time type error [...] In an automatically generated cast introduced to ensure the validity of an operation on a non-reifiable type और reference type casting):

class Erasure { 
public static <T> T unsafeIdentity(Object x) { 
    return (T) x; 
    //Compiled version: return (Object) x; 
    //optimised version: return x; 
} 

public static void main(String args[]) { 
    // This will fail on return, as the returned Object is type Object and Subtype Integer is expected, this results in an automatic cast and a ClassCastException: 
    Integer c = Erasure.<Integer>unsafeIdentity("foo"); 
    //Compiled version: Integer c = (Integer)Erasure.unsafeIdentity("foo"); 
    System.out.println(c.getClass().getName()); 
    } 
} 

typesafe संस्करण 3 विरासत कोड, तरीके जहाँ आप एक महाप्रकार हर जानते हैं (JLS The erasure of a type variable (§4.4) is the erasure of its leftmost bound.):

class Erasure { 
public static <T extends Integer> T unsafeIdentity(Object x) { 
    // This will fail due to Type erasure and incompatible types: 
    return (T) x; 
    // Compiled version: return (Integer) x; 
} 

public static void main(String args[]) { 
    //You should use Integer c = ... 
    Object c = Erasure.<Integer>unsafeIdentity("foo"); 
    System.out.println(c.getClass().getName()); 
    } 
} 

ऑब्जेक्ट का उपयोग केवल यह वर्णन करने के लिए किया गया था कि ऑब्जेक्ट संस्करण 1 और 3 में एक वैध असाइनमेंट लक्ष्य है, लेकिन यदि संभव हो तो आपको वास्तविक प्रकार या जेनेरिक प्रकार का उपयोग करना चाहिए।

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

+0

इस विषय के बारे में सोचा है, जो वास्तव में आप लिखते हैं वह वास्तव में सच है, लेकिन यह अभी भी –

+0

प्रश्न का उत्तर नहीं देता है "यह संकलक के सटीक नियम क्या हैं संकलन के दौरान डाला गया है जो प्रकार की सुरक्षा सुनिश्चित करता है और ये नियम कहां निर्दिष्ट हैं? " मैं या तो किसी भी संबंधित जेएलएस पैराग्राफ का संदर्भ देखने की उम्मीद करता हूं, या कई (कभी-कभी एज-केस) परिदृश्यों के अपघटन से कटौती किए गए कुछ शिक्षित अनुमान –

+0

मैंने संदर्भ जोड़े, मुझे केवल स्वचालित परिचय के लिए अप्रत्यक्ष संदर्भ मिला, लेकिन बाद के प्रकार संस्करण 2 में राइथेंड स्टेटमेंट के प्रकार को मिटाना ऑब्जेक्ट है, टाइप एरर के कारण। यह जेनेरिक ट्यूटोरियल दोनों में भी कहा गया है। – HopefullyHelpful

-1

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

अपने कोड में:

public class Erasure { 
    public static <T> T unsafeIdentity(Object x) { 
     return (T) x; 
    } 

    public static void main(String args[]) { 
     // I would expect it to fail: 
     Object c = Erasure.<Integer>unsafeIdentity("foo"); 
     System.out.println(c.getClass().getName()); 
     // but Prints 'java.lang.String' 
    } 
} 

यह संकलन समय के बाद जेनेरिक्स विलोपन होगा। संकलन समय पर, Erasure.unsafeIdentity में त्रुटियां नहीं हैं। जेवीएम मिट जेनिक्स जेनरिक्स पैराम्स पर निर्भर करता है जो आप देते हैं (इंटीजर)। उसके बाद, यह ?: तरह

public static Integer unsafeIdentity(Object x) { 
    return x; 
} 

वास्तव में समारोह है, covariant रिटर्न Bridge Methods जोड़ देगा:

public static Object unsafeIdentity(Object x) { 
    return x; 
} 

समारोह पिछले एक की तरह है, तो आप अपने मुख्य में कोड लगता है विधि संकलित विफल हो जाएगा? इसमें कोई गलती नहीं है। जेनरिक्स एरर इस फ़ंक्शन में कास्ट नहीं जोड़ पाएगा, और रिटर्न पैराम जावा फ़ंक्शन की इंडेंटिटी नहीं है।

मेरी व्याख्या थोड़ा दूर है, लेकिन उम्मीद आपको समझने में मदद कर सकती है।

संपादित करें:

के बाद उस विषय के बारे गूगल, मैं अपनी समस्याओं लगता है कि पुल तरीकों का उपयोग कर covariant वापसी प्रकार है। BridgeMethods

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