2014-09-26 56 views
0

對不起,TL更寬泛的版本; DR,但我覺得它需要一些解釋,否則會被人誤解。問題與異常類型的處理時不把他們 - 希望多抓

我有一個調用(通常是外部)代碼的方法,我希望有時會拋出一個RuntimeException,並使用可能拋出InterruptedException或ExecutionException的Future,並且我希望能夠返回一組有序的返回值從調用起直到拋出異常的值以及拋出的異常。我寫了一些有用的東西,但不幸的是,代碼看起來讓我覺得我做錯了什麼。我認爲我真正想要的是多捕獲成爲一個更通用的概念。這將使相當乾淨的代碼來解決這個問題,有點像這樣:

public class SomeResults { 
    private final Set<SomeReturnType> valuesReturned; 
    private final @Nullable RuntimeException | ExecutionException | InterruptedException exception; 

    public SomeResults(Set<SomeReturnType> valuesReturned, RuntimeException | ExecutionException exception { 
    this.valuesReturned = valuesReturned; 
    this.exception = exception; 
    } 

    public Set<SomeReturnType> getValuesReturned() { 
    return valuesReturned; 
    } 

    public @Nullable RuntimeException | ExecutionException | InterruptedException getException(); 
} 

而且有包裝了使調用外部代碼 的方法...

generateResults(Bar bar) { 
    // Setup code 
    Set<SomeReturnType> valuesReturned = new LinkedHashSet<>(); 
    ... 
    // loop 
    { 
    // stuff 
    ... // exceptions in this method should throw except for this one external code call 
    try { 
     valuesReturned.add(externalCodeCallGetSomeReturnValue(bar)) 
    } 
    catch(RuntimeException | ExecutionException | InterruptedException e) { 
     return new MyResults(valuesReturned, e) 
    } 
    ... 
    } 
    return new MyResults(valuesReturned, (RuntimeException | ExecutionException | InterruptedException) null); 
} 

並隨後做

SomeResults myResults = foo.generateResults(new Bar()); 
if(myResults.getException() != null) { 
    throw(myResults.getException); 
} 

等。另外,我也一直注意要立即重新拋出異常 - 這取決於誰在使用這些結果是什麼,他們會想與他們無關。我可能會做這樣的事情

try { 
    SomeResults myResults = foo.generateResults(new Bar()); 
    Foobar Foobar = new Foobar(myResults); 
} 
catch(Exception e) { 
    // I don't want to see any exceptions from externalCodeCallGetSomeReturnValue(bar) here 
    ... 
} 

當然,我可以讓例外獲得產生結果的函數拋出,而不是捕捉異常並返回其結果。這有兩個相當大的問題: 1.現在返回值的集合將是尷尬 - 我可以在設置或許傳遞給需要「迴歸」結果方法,它修改了設置返回一組代替。這允許設置在返回異常時保持最新。例如

generateResults(Bar bar, Set<SomeReturnType> orderedListForMeToWrite) throws ExecutionException, InterruptedException 
  • 如果代碼周圍的外部方法調用拋出運行時異常?現在我沒有簡單的方法來區分異常調用是從實際的調用到外部代碼還是其他東西!嘗試這種設計時,我遇到了這個問題。該代碼拋出拋出:IllegalArgumentException從別的地方,我的代碼處理對待它,就好像它已經從SomeReturnType externalCodeCallGetSomeReturnValue(酒吧吧)拋出。這似乎是一個代碼健康問題,這就是爲什麼我放棄這個解決方案。
  • 我是剛剛例外存儲爲異常去解決方案。但是,我討厭丟失這種類型的信息。沒有額外的代碼工作,如果有人想要拋出它,它將不得不聲明「拋出異常」,這是不好的,在那裏類似的代碼健康問題。有沒有一種處理這種情況的好方法? 我最終什麼事做,以得到它的工作我想它是結合的方式如下:

    public static class SomeResults { 
        private final Set<SomeReturnType> orderedReturnValues; 
        private final @Nullable Exception exception; 
    
        AsyncEchoesResult(Set<SomeReturnType> responses) { 
         this.orderedResponses = responses; 
         this.exception = null; 
        } 
    
        AsyncEchoesResult(Set<SomeReturnType> responses, RuntimeException exception) { 
         this.orderedResponses = responses; 
         this.exception = exception; 
        } 
    
        AsyncEchoesResult(Set<SomeReturnType> responses, ExecutionException exception) { 
         this.orderedResponses = responses; 
         this.exception = exception; 
        } 
    
        AsyncEchoesResult(Set<SomeReturnType> responses, InterruptedException exception) { 
         this.orderedResponses = responses; 
         this.exception = exception; 
        } 
    
        public Set<SomeReturnType> getResponses() { 
         return orderedResponses; 
        } 
    
        public @Nullable Exception getException() { 
         return exception; 
        } 
    
        public void throwExceptionIfExists() throws ExecutionException, InterruptedException { 
         try { 
         throw (exception); 
         } 
         catch (RuntimeException | ExecutionException | InterruptedException e) { 
         throw e; 
         } 
         catch (Exception e) { 
         throw new RuntimeException("Unexpected exception type in SomeResults",e); 
         } 
        } 
        } 
    

    很顯然,這是很醜陋的。如果我討厭的構造,因爲它們是我可以很輕鬆地用一個一個接受一個異常取代他們,但弱化類型檢查只throwException的運行時調用()。無論如何,有更好的替代方案嗎?請注意,我正在使用JDK 7,因此,雖然JDK 8的答案會很有趣,但無法解決我正在處理的問題。

    +0

    歡迎關於如何改進問題的建議。然而,沒有評論的downvote讓我沒有任何有用的信息。有人心情不好?有人不小心點擊了一個按鈕? – m24p 2014-09-26 21:48:08

    +0

    您的多構造函數方法不起作用,因爲您無法從多重catch子句調用它們中的任何一個。 multi-catch子句的代碼將捕獲的異常視爲具有公共基類的類型。只有在重新投擲時,纔會產生差異。 – Holger 2014-09-26 23:21:39

    +0

    我知道,這就是爲什麼我討厭這個想要更好的方法的原因之一。我調用構造函數的異常代碼看起來像是多執行前的日子。 :-( – m24p 2014-09-26 23:32:48

    回答

    1

    由於Java不允許一個變量聲明爲「這些類型之一的」你要封裝使用支持這種類型集的唯一構建體中的例外:一段代碼拋出該異常。

    考慮以下類型定義:

    interface ReThrower { 
        void reThrow() throws RuntimeException, ExecutionException, InterruptedException; 
    } 
    static class MyResult 
    { 
        private final Set<SomeReturnType> valuesReturned; 
        private final @Nullable ReThrower exception; 
    
        public MyResult(Set<SomeReturnType> valuesReturned, ReThrower exception) { 
        this.valuesReturned = valuesReturned; 
        this.exception = exception; 
        } 
    
        public Set<SomeReturnType> getValuesReturned() { 
        return valuesReturned; 
        } 
    
        public void reThrowException() 
        throws RuntimeException, ExecutionException, InterruptedException 
        { 
        if(exception!=null) exception.reThrow(); 
        } 
    } 
    

    然後你就可以創建一個MyResult這樣的:

    MyResult generateResults(Bar bar) { 
        // Setup code 
        Set<SomeReturnType> valuesReturned = new LinkedHashSet<>(); 
        // … 
        // loop 
        { 
        // stuff 
        // … exceptions in this method should throw except for this one external code call 
        try { 
         valuesReturned.add(externalCodeCallGetSomeReturnValue(bar)); 
        } 
        catch(RuntimeException | ExecutionException | InterruptedException e) { 
         // In Java 8 you would say: new MyResult(valuesReturned,()->{ throw e }); 
         return new MyResult(valuesReturned, new ReThrower() { 
         public void reThrow() 
          throws RuntimeException, ExecutionException, InterruptedException { 
          throw e; 
         } 
         }); 
        } 
        //... 
        } 
        return new MyResult(valuesReturned, null); 
    } 
    

    注意,內部類(或Java的8 lambda表達式)隱含存儲異常並且該隱式變量具有期望的「列出的異常類型之一」。然後,您可以安全地重新拋出異常:

    MyResult results = new MultiCatchAndStore().generateResults(new Bar()); 
    try 
    { 
        results.reThrowException(); 
    } catch(RuntimeException | ExecutionException | InterruptedException ex) 
    { 
        // handle, of course, you could also have separate catch clauses here 
    }