java - मरज - इश्क़ में मार्जवं स्टोरी




जावा 8: लैम्बडा एक्सप्रेशन में हैंडलिंग अनिवार्य चेक अपवाद। अनिवार्य क्यों नहीं, वैकल्पिक नहीं? (6)

मैं जावा 8 में नई लैम्ब्डा सुविधाओं के साथ खेल रहा हूं, और पाया कि जावा 8 द्वारा पेश की जाने वाली प्रथाएं वास्तव में उपयोगी हैं। हालांकि, मुझे आश्चर्य है कि निम्नलिखित परिदृश्य के लिए काम करने के लिए एक अच्छा तरीका है। मान लीजिए कि आपके पास ऑब्जेक्ट पूल रैपर है जिसके लिए ऑब्जेक्ट पूल भरने के लिए किसी प्रकार का कारखाना आवश्यक है, उदाहरण के लिए ( java.lang.functions.Factory का उपयोग करके):

public class JdbcConnectionPool extends ObjectPool<Connection> {

    public ConnectionPool(int maxConnections, String url) {
        super(new Factory<Connection>() {
            @Override
            public Connection make() {
                try {
                    return DriverManager.getConnection(url);
                } catch ( SQLException ex ) {
                    throw new RuntimeException(ex);
                }
            }
        }, maxConnections);
    }

}

कार्यात्मक इंटरफ़ेस को लैम्ब्डा अभिव्यक्ति में बदलने के बाद, उपरोक्त कोड इस तरह बन जाता है:

public class JdbcConnectionPool extends ObjectPool<Connection> {

    public ConnectionPool(int maxConnections, String url) {
        super(() -> {
            try {
                return DriverManager.getConnection(url);
            } catch ( SQLException ex ) {
                throw new RuntimeException(ex);
            }
        }, maxConnections);
    }

}

वास्तव में इतना बुरा नहीं है, लेकिन चेक अपवाद java.sql.SQLException को लैम्ब्डा के अंदर एक try / catch ब्लॉक try आवश्यकता है। मेरी कंपनी में हम लंबे समय तक दो इंटरफेस का उपयोग करते हैं:

  • IOut<T> यह java.lang.functions.Factory बराबर है। IOut<T> ;
  • और उन मामलों के लिए एक विशेष इंटरफ़ेस जो आमतौर पर चेक अपवाद प्रचार की आवश्यकता होती है: interface IUnsafeOut<T, E extends Throwable> { T out() throws E; } interface IUnsafeOut<T, E extends Throwable> { T out() throws E; }

IOut<T> और IUnsafeOut<T> को जावा 8 पर माइग्रेशन के दौरान हटाया जाना चाहिए, हालांकि IUnsafeOut<T, E> लिए कोई सटीक मिलान नहीं है। यदि लैम्ब्डा एक्सप्रेशन चेक अपवादों से निपट सकता है जैसे कि वे अनचेक किए गए थे, तो ऊपर दिए गए कन्स्ट्रक्टर में निम्न की तरह उपयोग करना संभव हो सकता है:

super(() -> DriverManager.getConnection(url), maxConnections);

यह बहुत साफ दिखता है। मैं देखता हूं कि मैं IUnsafeOut<T> सुपर क्लास को हमारे IUnsafeOut<T> को स्वीकार करने के लिए फिर से लिख सकता हूं, लेकिन जहां तक ​​मुझे पता है, जावा 8 अभी तक समाप्त नहीं हुआ है, इसलिए कुछ बदलाव हो सकते हैं जैसे:

  • IUnsafeOut<T, E> समान कुछ लागू करना? (ईमानदार होने के लिए, मैं उस गंदे पर विचार करता हूं - विषय को यह चुनना होगा कि क्या स्वीकार करना है: या तो Factory या "असुरक्षित फैक्ट्री" जिसमें संगत विधि हस्ताक्षर नहीं हो सकते हैं)
  • बस lambdas में चेक अपवादों को अनदेखा कर रहा है, तो IUnsafeOut<T, E> सरोगेट्स में कोई ज़रूरत नहीं है? (क्यों नहीं? उदाहरण के लिए एक और महत्वपूर्ण परिवर्तन: ओपनजेडीके, जिसका मैं उपयोग करता हूं, javac अब अज्ञात वर्ग [कार्यात्मक इंटरफ़ेस] या लैम्ब्डा अभिव्यक्ति में कैप्चर करने के लिए final रूप में घोषित करने के लिए चर और पैरामीटर की आवश्यकता नहीं होती है)

तो सवाल आम तौर पर है: क्या लैम्बडा में चेक अपवादों को बाईपास करने का कोई तरीका है या क्या भविष्य में यह योजना बनाई गई है जब तक कि जावा 8 अंत में रिलीज़ न हो जाए?

अद्यतन 1

एचएम-एमएम, जहां तक ​​मैं समझता हूं कि वर्तमान में हमारे पास क्या है, ऐसा लगता है कि इस समय कोई संदर्भ नहीं है, संदर्भित लेख 2010 से दिनांकित है: ब्रायन गोएट्ज़ जावा में अपवाद पारदर्शिता बताते हैं । यदि जावा 8 में कुछ भी नहीं बदला है, तो इसे एक उत्तर माना जा सकता है। इसके अलावा ब्रायन का कहना है कि interface ExceptionalCallable<V, E extends Exception> IUnsafeOut<T, E extends Throwable> interface ExceptionalCallable<V, E extends Exception> (जो मैंने IUnsafeOut<T, E extends Throwable> को हमारे कोड विरासत से बाहर IUnsafeOut<T, E extends Throwable> ) काफी बेकार है, और मैं उससे सहमत हूं।

क्या मुझे अभी भी कुछ और याद आ रहा है?


आप अपने लैम्ब्डा से फेंक सकते हैं , उन्हें सिर्फ "अपने तरीके से घोषित किया जाना है" (जो उन्हें दुर्भाग्य से मानक जेडीके कोड में पुनः उपयोग करने योग्य नहीं बनाता है, लेकिन हे, हम जो करते हैं वह हम करते हैं)।

@FunctionalInterface
public interface SupplierIOException {
   MyClass get() throws IOException;
}

या अधिक सामान्य आकार का संस्करण:

public interface ThrowingSupplier<T, E extends Exception> {
  T get() throws E;
}

here रेफरी here चेक अपवादों की घोषणा न करने के लिए "स्नीकी थ्रो" का उपयोग करने का भी उल्लेख है, लेकिन फिर भी उन्हें फेंक दें। यह मेरे सिर को थोड़ा सा दर्द देता है, शायद एक विकल्प।


क्या आपने लैम्ब्डा अभिव्यक्ति से मूल अपवाद को धुंधला करने के लिए रनटाइम अपवाद (अनचेक) रैपर क्लास का उपयोग करने पर विचार किया है, फिर लपेटा हुआ अपवाद वापस अपने मूल चेक अपवाद पर कास्टिंग कर रहा है?

class WrappedSqlException extends RuntimeException {
    static final long serialVersionUID = 20130808044800000L;
    public WrappedSqlException(SQLException cause) { super(cause); }
    public SQLException getSqlException() { return (SQLException) getCause(); }
}

public ConnectionPool(int maxConnections, String url) throws SQLException {
    try {
        super(() -> {
            try {
                return DriverManager.getConnection(url);
            } catch ( SQLException ex ) {
                throw new WrappedSqlException(ex);
            }
        }, maxConnections);
    } catch (WrappedSqlException wse) {
        throw wse.getSqlException();
    }
}

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

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


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

या आप अपना खुद का छोटा संयोजन लिख सकते हैं:

static<T> Supplier<T> exceptionWrappingSupplier(Supplier<T> b) {
     return e -> {
         try { b.accept(e); }
         catch (Exception e) { throw new RuntimeException(e); }
     };
}

आप इसे एक बार लिख सकते हैं, कम से कम यह आपके मूल ई-मेल को लिखने के लिए लिया गया था। और इसी तरह आप एक ही प्रकार के एसएएम के लिए उपयोग करते हैं।

मैं इसके बजाए इसे विकल्प के बजाय "ग्लास 99% पूर्ण" के रूप में देखता हूं। सभी समस्याओं को समाधान के रूप में नई भाषा सुविधाओं की आवश्यकता नहीं है। (उल्लेख नहीं है कि नई भाषा सुविधाओं हमेशा नई समस्याओं का कारण बनती है।)

उन दिनों उपभोक्ता इंटरफ़ेस को ब्लॉक कहा जाता था।

मुझे लगता है कि यह जेबी निजेट के जवाब से मेल खाता है।

बाद में ब्रायन बताते हैं कि यह इस तरह से डिजाइन क्यों किया गया था (समस्या का कारण)

हां, आपको अपने असाधारण एसएएम प्रदान करना होगा। लेकिन फिर लैम्ब्डा रूपांतरण उनके साथ ठीक काम करेगा।

ईजी ने इस समस्या के लिए अतिरिक्त भाषा और लाइब्रेरी समर्थन पर चर्चा की, और अंत में महसूस किया कि यह एक खराब लागत / लाभ व्यापार था।

लाइब्रेरी-आधारित समाधान एसएएम प्रकारों (असाधारण बनाम नहीं) में 2x विस्फोट का कारण बनते हैं, जो प्राचीन विशेषज्ञता के लिए मौजूदा संयोजी विस्फोटों के साथ बुरी तरह से बातचीत करते हैं।

उपलब्ध भाषा-आधारित समाधान जटिलता / मूल्य व्यापार से हार गए थे। यद्यपि कुछ वैकल्पिक समाधान हैं, लेकिन हम अन्वेषण जारी रखने जा रहे हैं - हालांकि स्पष्ट रूप से 8 के लिए नहीं और शायद 9 के लिए नहीं।

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


वर्णित तरीके से अपवाद को लपेटना काम नहीं करता है। मैंने कोशिश की और मुझे अभी भी कंपाइलर त्रुटियां मिलती हैं, जो वास्तव में spec के अनुसार होती है: लैम्ब्डा अभिव्यक्ति अपवाद फेंकता है जो विधि तर्क के लक्ष्य प्रकार के साथ असंगत है: कॉल करने योग्य; कॉल () इसे फेंक नहीं देता है इसलिए मैं लैम्ब्डा अभिव्यक्ति को कॉल करने योग्य के रूप में पास नहीं कर सकता।

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


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

आप देखेंगे कि जावा 8 के साथ शामिल 43 इंटरफेस बनाम पागुरो बनाम केवल 4 कार्यात्मक इंटरफेस हैं। ऐसा इसलिए है क्योंकि पगुरो जेनेरिक को प्राइमेटिव पसंद करते हैं।

पगुरो के पास अपने अपरिवर्तनीय संग्रह (क्लोजर से कॉपी) में निर्मित सिंगल-पास ट्रांसफॉर्मेशन हैं। ये परिवर्तन लगभग क्लोजर ट्रांसड्यूसर या जावा 8 स्ट्रीम के बराबर हैं, लेकिन वे कार्यात्मक इंटरफेस स्वीकार करते हैं जो चेक अपवादों को लपेटते हैं। देखें: पागुरो और जावा 8 धाराओं के बीच अंतर


सितंबर 2015:

आप इसके लिए ET उपयोग कर सकते हैं। अपवाद रूपांतरण / अनुवाद के लिए ईटी एक छोटी जावा 8 लाइब्रेरी है।

ईटी के साथ आप लिख सकते हैं:

super(() -> et.withReturningTranslation(() -> DriverManager.getConnection(url)), maxConnections);

मल्टी लाइन संस्करण:

super(() -> {
  return et.withReturningTranslation(() -> DriverManager.getConnection(url));
}, maxConnections);

आपको पहले कुछ करने की ज़रूरत है, एक नया ExceptionTranslator उदाहरण बना रहा है:

ExceptionTranslator et = ET.newConfiguration().done();

यह उदाहरण थ्रेड सुरक्षित है जिसे एकाधिक घटकों द्वारा साझा किया जा सकता है। यदि आप चाहें तो आप अधिक विशिष्ट अपवाद रूपांतरण नियम (जैसे FooCheckedException -> BarRuntimeException ) को कॉन्फ़िगर कर सकते हैं। यदि कोई अन्य नियम उपलब्ध नहीं है, तो चेक अपवाद स्वचालित रूप से RuntimeException परिवर्तित हो जाते हैं।

(अस्वीकरण: मैं ईटी का लेखक हूं)







java-8