2010-08-27 22 views
17

कोशिश करें मैं एक कोशिश ब्लॉक में परिवर्तनीय दायरे के बारे में नियम से नाखुश हूं, जो संबंधित पकड़ और आखिरकार ब्लॉक के साथ साझा नहीं किया जा रहा है। विशेष रूप से वह ऐसा कोड की ओर जाता है:ब्लॉक स्कोप

var v: VType = null 

try { 
    v = new VType() 
} 
catch { 
    case e => // handle VType constructor failure (can reference v) 
} 
finally { 
    // can reference v. 
} 

के रूप में करने का विरोध किया:

try { 
    val v = new VType() 
} 
catch { 
    case e => // handle VType constructor failure (can reference v) 
} 
finally { 
    // can reference v. 
} 

किसी को समझाने या औचित्य साबित क्यों जावा से इस नियम बनी रहती है सकते हैं?

और/या उम्मीद है कि यह बदल सकता है?

धन्यवाद!

अद्यतन

तारीख करने के लिए सभी प्रतिक्रिया के लिए बहुत धन्यवाद।

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

मुझे रेक्स केर का जवाब पसंद है लेकिन उपरोक्त मूल कोड विधि विधि में स्थानीय var को पेश किए बिना विधि कॉल में कैसे लपेटा जाएगा?

मेरे स्वयं के प्रयास बहुत ही अच्छे नहीं थे, निर्माण नाम में सुरक्षित रूप से निर्माण तक देरी के लिए एक उप-पैरामीटर पैरामीटर का उपयोग करके, लेकिन अभी भी मुझे कैच में निर्मित (या नहीं) ऑब्जेक्ट तक पहुंच नहीं देता है ब्लॉक।

+1

अच्छा सवाल। ऐसा लगता है कि आपको 'var' पेश करना है और आप 'try-catch-finally'-block में 'val' का उपयोग नहीं कर सकते हैं, जहां आपको कुछ संसाधनों को साफ़ करने की आवश्यकता है या जो भी हो। –

+0

ऐसा लगता है कि कैच स्टेटमेंट द्वारा डिज़ाइन आदर्श के लिए नहीं बनाता है ... कैच स्टेटमेंट में परिभाषित ऑब्जेक्ट उत्पन्न करना आदर्श है क्योंकि यह एक तेज प्रक्रिया है और असफल नेटवर्क अनुरोध आदि से त्वरित पुनर्प्राप्ति प्रदान करता है। – Alex

+0

चाहते हैं एक नोट बनाओ, कि यदि VType कन्स्ट्रक्टर विफल हुआ है तो आप v वैरिएबल को स्पर्श नहीं करना चाहते हैं, जब तक कि आप एक बड़ी खाली नल देखना न चाहें। scala.util.control.Exception – jsuereth

उत्तर

14

आप गलत तरीके से समस्या के बारे में सोच रहे होंगे। आप अपने प्रयास/पकड़/अंत में ब्लॉक में इतनी सारी चीज़ें क्यों चाहते हैं? अपने कोड में,

try { val v = new VType() } 

अपवाद फेंक दिया जा सकता है इससे पहले कि आप v वापस मिलता है, तो आप सुरक्षित रूप से संदर्भ नहीं दे सकता v। लेकिन यदि आप v का संदर्भ नहीं दे सकते हैं, तो अंत में आप क्या कर सकते हैं जो अपना अपवाद तोड़ने या फेंकने या कुछ अन्य बीमार परिभाषित व्यवहार नहीं करेगा? क्या होगा यदि आप v बनाते हैं लेकिन w बनाने में विफल रहते हैं, लेकिन निपटान के लिए w भी होना आवश्यक है? (या नहीं?) यह एक गड़बड़ होने के समाप्त होता है।

लेकिन यदि आप जावा से आ रहे हैं, तो कुछ ऐसी चीजें हैं जो आपको समझदार तरीके से कोशिश/पकड़/आखिरकार ब्लॉक लिखने में मदद कर सकती हैं।

def s2a(s: String) = try { Some(s.toInt) } catch { case nfe: NumberFormatException => None} 

एक और बात आप कर सकते हैं अपने खुद के संसाधन प्रबंधक

def enclosed[C <: { def close() }](c: C)(f: C => Unit) { 
    try { f(c) } finally { c.close() } 
} 
enclosed(new FileInputStream(myFile))(fis => { 
    fis.read... 
} 

या आप बनाने के लिए है:

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

val r = valuableOpenResource() 
def attempt[F](f: => F) = { 
    try { f } catch { case re: ReasonableException => r.close() throw re } 
} 
doSomethingSafe() 
attempt(doSomethingDangerous()) 
doSomethingElseSafe() 
r.close() 

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

+0

आपकी सलाह और उदाहरणों के लिए बहुत धन्यवाद, जहां तक ​​एक शैली का संबंध है, मैं बहस नहीं कर सकता। ऐसा लगता है कि यह एक ऐसा है जो जावा मुहावरे के स्कैला संस्करण (यदि आप जानते हैं कि मेरा क्या मतलब है) दूर हो गया है और ब्लॉक को कोशिश करने के लिए कुछ समय की तरह छिपाने के लिए कुछ हैं। –

+0

एक सुझाव: संलग्न() विधि में {def close()} का उपयोग करने के बजाय, हम java.io.Closeable http://download.oracle.com/javase/6/docs/api/java/io/Closeable का उपयोग कर सकते हैं .html –

+0

@Marimuthu {def close()} का उपयोग करना बेहतर हो सकता है क्योंकि यह आपको संसाधनों को प्रबंधित करने की अनुमति देता है जो निकटतम अनुबंध को पूरा करते हैं लेकिन 'java.io.Closeable' इंटरफ़ेस को लागू नहीं करते हैं। –

6

यह कोड कैसे काम करेगा?

try 
{ 
    int i = 0; 

    // Do stuff... 

    Foo x = new Foo(); 

    // Do more stuff... 

    Bar y = new Bar(); 
} 
catch 
{ 
    // Print the values of i, x, and y. 
} 

i, x, और y के मान क्या हैं? क्या हम कैच ब्लॉक में उतरने से पहले भी घोषित हो गए थे?

+0

दिलचस्प बिंदु, धन्यवाद, हालांकि मुझे लगता है कि गुंजाइश एक संकलन समय मुद्दा है, यदि ऐसा है तो वर्र्स या वैल्स ज्ञात होंगे, भले ही उनके मूल्य अनिश्चित हैं। –

+0

@ डॉन - मुझे लगता है कि आप इस संबंध में मौलिक रूप से गलत समझने के दायरे हैं; दायरा संकलक को आपके कोड में किसी बिंदु पर कुछ मूल्यों के लिए * प्रारंभ * के बारे में तर्क करने की अनुमति देता है। जॉन बी के उदाहरण में, वह दर्शाता है कि संकलक यह सुनिश्चित नहीं कर सकता कि 'i', 'x' या' y' में से कोई भी –

+0

@oxbowlakes प्रारंभ किया गया है, मैं आपका बिंदु लेता हूं, अनिश्चित मान संकलक के लिए अस्वीकार्य हैं। मैं जावा में घोषित सदस्यों के लिए डिफ़ॉल्ट रूप से सोच रहा था (मुझे पता है कि यह स्थानीय लोगों पर लागू नहीं होता है) कोशिश ब्लॉक में आवेदन करना संभव हो सकता है। मुझे लगता है कि हासिल करना असंभव नहीं है लेकिन मुझे लगता है कि यह इंप्रेशन प्राप्त नहीं कर रहा है। –

4

अपवाद अवधारणा प्रयास ब्लॉक का सबराउटिन नहीं है, यह एक वैकल्पिक कोड प्रवाह है। यह कोशिश-पकड़ नियंत्रण ब्लॉक को "अगर कुछ भी अवांछित होता है" की तरह अधिक बनाता है तो आवश्यकतानुसार प्रयास ब्लॉक की वर्तमान स्थिति में इन (पकड़ें) रेखाएं डालें।

यह ध्यान में रखते हुए, यह स्पष्ट नहीं है कि Val v = Type(); परिभाषित किया जा रहा है या नहीं क्योंकि अपवाद (सैद्धांतिक रूप से) Val v = Type(); से पहले फेंक दिया जा सकता है। हां, Val v ब्लॉक में पहली पंक्ति है, लेकिन जेवीएम त्रुटियां हैं जिन्हें इससे पहले फेंक दिया जा सकता है।

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

एकमात्र वैकल्पिक बाएं (अब हम अस्तित्व की अनिश्चितता के कारण ब्लॉक चर का उपयोग नहीं कर सकते हैं) पूरे कोड ब्लॉक के बीच संचार के लिए पूरे प्रयास-अंत-निर्माण के बाहर चर का उपयोग करना है।

क्या यह चूसता है? थोड़ा और छोटा हो सकता है। क्या हमारे पास कुछ भी बेहतर है? शायद ऩही। ब्लॉक के बाहर परिवर्तनीय घोषणाओं को रखना यह स्पष्ट करता है कि चर-बारी-अंत परिदृश्य में जो भी नियंत्रण संरचना आप प्रक्रिया करते हैं, उससे पहले वैरिएबल को परिभाषित किया जाएगा।

case class VType(name: String) { 
    // ... maybe throw an exception ... 
} 

val v = LazyVal(() => new VType()) 
try { 
    // do stuff with v 
    println(v.name) // implicitly converts LazyVal[VType] to VType 

    // do other unsafe stuff 
} catch { 
    case e => // handle VType constructor failure 
    // can reference v after verifying v.isInitialized 
} finally { 
    // can reference v after verifying v.isInitialized 
    if (v.isInitialized) v.safelyReleaseResources 
} 

जहां LazyVal रूप

/** 
* Based on DelayedLazyVal in the standard library 
*/ 
class LazyVal[T](f:() => T) { 
    @volatile private[this] var _inited = false 
    private[this] lazy val complete = { 
     val v = f() 
     _inited = true 
     v 
    } 

    /** Whether the computation is complete. 
    * 
    * @return true if the computation is complete. 
    */ 
    def isInitialized = _inited 

    /** The result of f(). 
    * 
    * @return the result 
    */ 
    def apply(): T = complete 
} 

object LazyVal { 
    def apply[T](f:() => T) = new LazyVal(f) 
    implicit def lazyval2val[T](l: LazyVal[T]): T = l() 
} 

परिभाषित किया गया है यह है कि अगर हम अच्छा होगा:

+0

उत्तर के लिए धन्यवाद, मुझे अभी भी विश्वास नहीं है कि कोशिश ब्लॉक ब्लॉक और वर्र्स उपलब्ध नहीं हैं, उनके मूल्य निर्धारित किए जा सकते हैं या नहीं भी हो सकते हैं, लेकिन मुझे लगता है कि अपवाद हैंडलिंग में यह एक उपयोगी धारणा है जब तक परिवर्तनीय स्थिति न हो प्रख्यात रूप से जाना जाता है। जहां तक ​​मुझे पता है, अपवाद को उठाए जाने वाले प्रयास ब्लॉक में स्थान से एक गैर-स्थानीय कूद द्वारा कैच ब्लॉक तक पहुंच जाता है। कैच ब्लॉक के पास पूर्ण दायरे तक पहुंच है और इसमें ब्लॉक ब्लॉक वाले ब्लॉक शामिल हैं, तो कोशिश भी ब्लॉक क्यों नहीं करें? –

+0

"हो सकता है या नहीं" के लिए एक कंपाइलर चेक कैसे उपलब्ध हो सकता है? एकमात्र विकल्प वैरिएबल अनुपलब्ध मानना ​​है, क्योंकि उनके उपलब्ध होने पर कोई विचार गारंटी नहीं है। –

3

यदि आपका मुख्य चिंता यह है कि v अपरिवर्तनीय होना चाहिए, तुम क्या आप के साथ चाहते हैं उसके पास मिल सकता है lazy val v = new VType() का उपयोग कर सकते हैं, लेकिन AFAIK सुरक्षित रूप से निर्धारित करने के लिए कोई तंत्र नहीं है कि lazy val प्रारंभ किया गया है या नहीं।

+0

उत्तर के लिए धन्यवाद, इस सुरुचिपूर्ण तकनीक के पते ने मुझे इस मुद्दे के बारे में पहली जगह से नाखुश बना दिया लेकिन मैं दायरे प्रतिबंध के बारे में उत्सुक हूं। –

+0

कोशिश-अंत-अंतराल ब्लॉक के लिए स्कोपिंग नियमों को बदलने से यह अन्य ब्लॉक (अगर-अन्य, नेस्टेड फ़ंक्शंस इत्यादि) के साथ असंगत हो जाएगा, और, जैसा कि रेक्स ने इंगित किया है, ऐसे चर को एक्सेस करते समय संभावित आश्चर्यों का कारण बन सकता है जो हो सकता है शुरू किया गया –

+0

आलसी प्रारंभिक तंत्र को दोहराने की कोई आवश्यकता नहीं है, बस 'आलसी वैल v = f' का उपयोग करें और, यदि आप अभी भी इसे चाहते हैं,' def लागू करें(): टी = v'। –

2

आपका उदाहरण ठोस नहीं है कि आपको अंतिम खंड की आवश्यकता क्यों है। यदि वीटी टाइप उदाहरण है एक संसाधन जिसे बंद करने की आवश्यकता है, आप इसे निम्न तरीकों में से एक कर सकते हैं। ,

try { 
    val v = new VType // may throw 
    try { 
    v.someThing // may throw 
    } 
    finally { 
    v.close() 
    } 
} 
catch { 
    case ex => println("Error on either operation: " + ex) 
} 

या तो मामले में:

try { 
    val v = new VType // may throw 
    try { 
    v.someThing // may throw 
    } 
    catch { 
    case ex => println("Error on doing something with v :" + v + ex) // or whatever 
    } 
    finally { 
    v.close() 
    } 
} 
catch { 
    case ex => println("Error on getting or closing v: " + ex) // v might not be constructed 
} 

2) आप पकड़ खंड में वी के बारे में परवाह नहीं है:

1) आप इसे एक अपवाद फेंकता उपयोग करने के बाद वी संदर्भ लेना चाहते हैं आप var से छुटकारा पाएं।

object Guard { 
    type Closing = {def close:Unit} 

    var guarded: Stack[Set[Closing]] = Stack() 
    def unapply(c: Closing) = { 
     guarded.push(guarded.pop + c) 
     Some(c) 
    } 

    private def close {println("Closing"); guarded.head.foreach{c => c.close}} 
    private def down {println("Adding Set"); guarded.push(Set())} 
    private def up {println("Removing Set"); guarded.pop} 

    def carefully(f: => Unit) { 
     down 
     try {f} 
     finally {close; up} 
    } 
} 

आप इस तरह इसका इस्तेमाल कर सकते हैं::

3

यहाँ एक और विकल्प है

import Guard.carefully 

class File {def close {println("Closed File")}} 
class BadFile {def close {println("Closed Bad File")}; throw new Exception("BadFile failed")} 

carefully { 
    val Guard(f) = new File 
    val Guard(g) = new File 
    val Guard(h) = new BadFile 
} 

जो

सेट

समापन जोड़ने में जो परिणाम 210

बंद फ़ाइल

बंद फ़ाइल

java.lang.Exception: BadFile में विफल रहा है

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

+0

यह प्रभावशाली और बहुत रचनात्मक है। मैं विशेष रूप से वाल्ट प्राप्त करने के लिए एक्स्ट्रेक्टर तकनीक पसंद करता हूं और कैसे कोई अपवाद सावधानीपूर्वक फैलता है। –

+0

धन्यवाद, एक चेतावनी; यह थ्रेडसेफ लिखित नहीं है, इसलिए उदाहरण के लिए, कलाकारों का उपयोग करने वाले एप्लिकेशन में देखभाल के साथ संभाल लें। – Magnus

20

बस इस "कोशिश";)

val v = try { new VType() } catch { case e: Exception => /* ... */ } 

स्काला में, try एक अभिव्यक्ति है, तो यह एक मूल्य है।

+1

अत्यधिक वास्तुकला अपने सभी दुश्मन हैं, और यह समाधान एक समाधान को overengineer के प्रलोभन से बचाता है। कुडोस। – Ichimonji10

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