2014-09-03 13 views
44

मैं जावा 8 के साथ प्रोजेक्ट पर काम कर रहा हूं और एक ऐसी स्थिति पाई जिसे मैं समझ नहीं पा रहा हूं।जावा 8 विधि संदर्भ अनचाहे अपवाद

मैं इस तरह कोड है:

void deleteEntity(Node node) throws SomeException { 
    for (ChildNode child: node.getChildren()) { 
     deleteChild(child); 
    } 
} 

void deleteChild(Object child) throws SomeException { 
    //some code 
} 

इस कोड को ठीक काम कर रहा है, लेकिन मैं एक विधि संदर्भ में यह पुनर्लेखन कर सकते हैं:

void deleteEntity(Node node) throws SomeException { 
    node.getChildren().forEach(this::deleteChild); 
} 

और इस कोड संकलन नहीं करता है, दे रही है त्रुटि Incompatible thrown types *SomeException* in method reference

आईडीईए ने मुझे त्रुटि unhandled exception त्रुटि दी।

तो, मेरा सवाल है क्यों? कोड प्रत्येक लूप के लिए क्यों संकलित करता है और लैम्ब्डा के साथ संकलित नहीं करता है?

+10

एक तरफ के रूप में, यह एक लैम्ब्डा अभिव्यक्ति नहीं है - यह एक विधि संदर्भ है। यदि आप 'forEach (x -> deleteChild (x)) 'का उपयोग करते हैं तो यह एक लैम्ब्डा अभिव्यक्ति होगी। हालांकि यह एक ही कारण के लिए असफल हो जाएगा। –

उत्तर

49

आप Consumer<T> इंटरफेस को देखें, तो accept विधि (जो कि आपके विधि संदर्भ प्रभावी ढंग से उपयोग किया जाएगा) घोषित नहीं किया गया है किसी भी अपवाद जाँच फेंकने के लिए - इसलिए यदि आप एक विधि संदर्भ जो है उपयोग नहीं कर सकते एक चेक अपवाद फेंकने की घोषणा की। लूप के लिए बढ़ाया ठीक है, क्योंकि वहां आप हमेशा एक संदर्भ में रहते हैं जहां SomeException फेंक दिया जा सकता है।

आप संभावित रूप से एक रैपर बना सकते हैं जो चेक अपवाद को एक अनचेक अपवाद में परिवर्तित करता है, और उसे फेंक देता है। वैकल्पिक रूप से, आप एक accept() तरीका है जिसके (शायद कि अपवाद के साथ इंटरफेस parameterizing) एक जाँच अपवाद फेंक करता है के साथ अपने स्वयं कार्यात्मक इंटरफ़ेस की घोषणा कर सकता है, और फिर अपने खुद के forEach विधि है कि एक इनपुट के रूप कि कार्यात्मक इंटरफ़ेस लेता लिखें।

+0

आपके प्रश्न के लिए आपके प्रश्न/Thx के लिए हाय Thx। जावा 8 और ऊपर से चेक अपवादों का उपयोग न करने के बारे में क्या? – avi613

+0

@ avi613: मुझे यकीन नहीं है कि आपका क्या मतलब है, लेकिन अपवादों की जांच वैकल्पिक नहीं है ... –

+0

बेशक वे नहीं हैं! :) मैं चेक वी के बारे में असहमत लोगों के बारे में पढ़ रहा हूं। अनचेक अपवाद। [उदाहरण] के लिए देखें (http://docs.oracle.com/javase/tutorial/essential/exceptions/runtime.html)। यहां ओरेकल दस्तावेज़ चेक अपवादों का उपयोग करने के बारे में बहुत ही अंतिम है। हालांकि वे लैम्बडास के उपयोग पर लगाए गए सीमा के अपवाद का उल्लेख करते हैं। मैं सोच रहा था कि क्या यह सीमा चेक अपवादों का उपयोग करने से बचने के लिए पर्याप्त खराब हो सकती है। – avi613

9

आप इस कोशिश कर सकते हैं:

void deleteEntity(Node node) throws SomeException {  node.getChildren().forEach(UtilException.rethrowConsumer(this::deleteChild)); 
    } 

UtilException सहायक वर्ग नीचे आप किसी भी जावा में जांचे हुए अपवादों धाराओं का उपयोग करने देता है। ध्यान दें कि ऊपर की धारा this::deleteChild द्वारा फेंक दिया गया मूल चेक अपवाद भी फेंकता है, और कुछ लपेटने वाले अपवाद को अपनाने के लिए नहीं।

public final class UtilException { 

@FunctionalInterface 
public interface Consumer_WithExceptions<T, E extends Exception> { 
    void accept(T t) throws E; 
    } 

@FunctionalInterface 
public interface BiConsumer_WithExceptions<T, U, E extends Exception> { 
    void accept(T t, U u) throws E; 
    } 

@FunctionalInterface 
public interface Function_WithExceptions<T, R, E extends Exception> { 
    R apply(T t) throws E; 
    } 

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

@FunctionalInterface 
public interface Runnable_WithExceptions<E extends Exception> { 
    void run() throws E; 
    } 

/** .forEach(rethrowConsumer(name -> System.out.println(Class.forName(name)))); or .forEach(rethrowConsumer(ClassNameUtil::println)); */ 
public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) throws E { 
    return t -> { 
     try { consumer.accept(t); } 
     catch (Exception exception) { throwAsUnchecked(exception); } 
     }; 
    } 

public static <T, U, E extends Exception> BiConsumer<T, U> rethrowBiConsumer(BiConsumer_WithExceptions<T, U, E> biConsumer) throws E { 
    return (t, u) -> { 
     try { biConsumer.accept(t, u); } 
     catch (Exception exception) { throwAsUnchecked(exception); } 
     }; 
    } 

/** .map(rethrowFunction(name -> Class.forName(name))) or .map(rethrowFunction(Class::forName)) */ 
public static <T, R, E extends Exception> Function<T, R> rethrowFunction(Function_WithExceptions<T, R, E> function) throws E { 
    return t -> { 
     try { return function.apply(t); } 
     catch (Exception exception) { throwAsUnchecked(exception); return null; } 
     }; 
    } 

/** rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))), */ 
public static <T, E extends Exception> Supplier<T> rethrowSupplier(Supplier_WithExceptions<T, E> function) throws E { 
    return() -> { 
     try { return function.get(); } 
     catch (Exception exception) { throwAsUnchecked(exception); return null; } 
     }; 
    } 

/** uncheck(() -> Class.forName("xxx")); */ 
public static void uncheck(Runnable_WithExceptions t) 
    { 
    try { t.run(); } 
    catch (Exception exception) { throwAsUnchecked(exception); } 
    } 

/** uncheck(() -> Class.forName("xxx")); */ 
public static <R, E extends Exception> R uncheck(Supplier_WithExceptions<R, E> supplier) 
    { 
    try { return supplier.get(); } 
    catch (Exception exception) { throwAsUnchecked(exception); return null; } 
    } 

/** uncheck(Class::forName, "xxx"); */ 
public static <T, R, E extends Exception> R uncheck(Function_WithExceptions<T, R, E> function, T t) { 
    try { return function.apply(t); } 
    catch (Exception exception) { throwAsUnchecked(exception); return null; } 
    } 

@SuppressWarnings ("unchecked") 
private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E { throw (E)exception; } 

} 

कैसे (स्थिर UtilException आयात करने के बाद) इसका इस्तेमाल करने पर कई अन्य उदाहरण:

@Test 
public void test_Consumer_with_checked_exceptions() throws IllegalAccessException { 
    Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String") 
      .forEach(rethrowConsumer(className -> System.out.println(Class.forName(className)))); 

    Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String") 
      .forEach(rethrowConsumer(System.out::println)); 
    } 

@Test 
public void test_Function_with_checked_exceptions() throws ClassNotFoundException { 
    List<Class> classes1 
      = Stream.of("Object", "Integer", "String") 
        .map(rethrowFunction(className -> Class.forName("java.lang." + className))) 
        .collect(Collectors.toList()); 

    List<Class> classes2 
      = Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String") 
        .map(rethrowFunction(Class::forName)) 
        .collect(Collectors.toList()); 
    } 

@Test 
public void test_Supplier_with_checked_exceptions() throws ClassNotFoundException { 
    Collector.of(
      rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))), 
      StringJoiner::add, StringJoiner::merge, StringJoiner::toString); 
    } 

@Test  
public void test_uncheck_exception_thrown_by_method() { 
    Class clazz1 = uncheck(() -> Class.forName("java.lang.String")); 

    Class clazz2 = uncheck(Class::forName, "java.lang.String"); 
    } 

@Test (expected = ClassNotFoundException.class) 
public void test_if_correct_exception_is_still_thrown_by_method() { 
    Class clazz3 = uncheck(Class::forName, "INVALID"); 
    } 

लेकिन निम्न लाभ, नुकसान, और सीमाओं को समझने से पहले उसका उपयोग नहीं करते:

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

• यदि कॉलिंग कोड पहले ही चेक अपवाद को संभालता है, तो संकलक आपको विधि घोषणा में फेंकने वाला क्लॉज जोड़ने के लिए याद दिलाएगा जिसमें धारा है (यदि आप यह नहीं कहेंगे: अपवाद कभी नहीं फेंक दिया जाता है संबंधित प्रयास कथन का शरीर)।

• किसी भी मामले में, आप धारा (यदि आप कोशिश करते हैं, तो संकलक कहेंगे, अपवाद को कभी भी शरीर में नहीं फेंक दिया गया है) के अंदर चेक अपवाद को पकड़ने के लिए स्ट्रीम को घेरने में सक्षम नहीं होगा संबंधित प्रयास कथन)।

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

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

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

• किसी भी मामले में, अगर आप को जोड़ने के लिए नहीं है (या जोड़ने के लिए भूल जाते हैं) के लिए जाँच की अपवाद विधि है कि धारा, जांचे हुए अपवादों फेंकने की इन 2 परिणामों के बारे में पता होना होता है के खंड फेंकता तय:

1) कॉलिंग कोड नाम से इसे पकड़ने में सक्षम नहीं होगा (यदि आप कोशिश करते हैं, तो संकलक कहेंगे: प्रासंगिकता कथन के प्रयास में अपवाद कभी नहीं फेंक दिया जाता है)। यह बबल होगा और शायद कुछ "कैच अपवाद" या "कैच थ्रोबल" द्वारा मुख्य प्रोग्राम लूप में पकड़ा जाएगा, जो कि आप वैसे भी हो सकते हैं।

2) यह कम से कम आश्चर्य के सिद्धांत का उल्लंघन करता है: यह अब संभावित अपवादों को पकड़ने की गारंटी देने में सक्षम होने के लिए RuntimeException को पकड़ने के लिए पर्याप्त नहीं होगा। इस कारण से, मेरा मानना ​​है कि यह फ्रेमवर्क कोड में नहीं किया जाना चाहिए, लेकिन केवल उस व्यवसाय कोड में जिसे आप पूरी तरह से नियंत्रित करते हैं।

निष्कर्ष में: मेरा मानना ​​है कि यहां सीमाएं गंभीर नहीं हैं, और UtilException कक्षा का डर के बिना उपयोग किया जा सकता है। हालांकि, यह आप पर निर्भर है!

0

तुम भी someException घोषित कर सकता है इतना है कि यह RuntimeException बजाय Exception फैली हुई है। निम्न उदाहरण कोड संकलन होगा:

public class Test { 

    public static void main(String[] args){ 
     // TODO Auto-generated method stub 
     List<String> test = new ArrayList<String>(); 
     test.add("foo"); 
     test.add(null); 
     test.add("bar"); 
     test.forEach(x -> print(x));  
    } 

    public static class SomeException extends RuntimeException{ 
    } 

    public static void print(String s) throws SomeException{ 
     if (s==null) throw new SomeException(); 
     System.out.println(s); 
    } 
} 

उत्पादन तो हो जाएगा:

foo 
Exception in thread "main" simpleTextLayout.Test$SomeException 
at simpleTextLayout.Test.print(Test.java:22) 
at simpleTextLayout.Test.lambda$0(Test.java:14) 
at java.util.ArrayList.forEach(ArrayList.java:1249) 
at simpleTextLayout.Test.main(Test.java:14) 

हालांकि forEach बयान के निष्पादन forEach बयान के चारों ओर एक try/catch ब्लॉक जोड़ सकते हैं एक बार एक बाधित हो जाएगा अपवाद फेंक दिया गया है। उपर्युक्त उदाहरण में, सूची के "bar" तत्व मुद्रित नहीं किए जाएंगे। इसके अलावा, ऐसा करके, आप अपने आईडीई में फेंकने वाले अपवाद का ट्रैक खो देंगे।

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