checked-exceptions Java 8:在lambda表達式中強制檢查異常處理。 為什麼強制,不是可選的?




4 Answers

在lambda郵件列表中,對此進行了徹底的討論 。 正如你所看到的那樣,Brian Goetz建議選擇編寫自己的組合器:

或者你可以寫自己的瑣碎組合:

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

您可以編寫一次,而不是寫入原始電子郵件所花費的時間。 同樣適用於您使用的每種SAM。

我寧願把它看作“玻璃99%滿”而不是替代品。 並非所有問題都需要新的語言功能作為解決方 (更不用說新語言功能總會導致新問題。)

在那些日子裡,Consumer界面被稱為Block。

我認為這符合JB Nizet的答案

後來Brian 解釋了為什麼這是這樣設計的(問題的原因)

是的,您必須提供自己的特殊SAM。 但是lambda轉換對它們來說可以正常工作。

EG討論了此問題的其他語言和庫支持,最終認為這是一個糟糕的成本/收益權衡。

基於庫的解決方案導致SAM類型爆炸2倍(異常與非),與原始專業化的現有組合爆炸相互作用很嚴重。

可用的基於語言的解決方案是複雜性/價值權衡的輸家。 雖然有一些替代解決方案我們將繼續探索 - 雖然顯然不是8,也可能不是9。

在此期間,您可以使用工具來執行您想要的操作。 我知道你更喜歡我們為你提供最後一英里(其次,你的請求實際上是一個薄薄的請求“你為什麼不放棄已檢查的異常”),但我認為當前狀態讓我們你完成了你的工作。

java lambda checked-exceptions java-8

我正在使用Java 8中的新lambda特性,並發現Java 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);
    }

}

將函數接口轉換為lambda表達式後,上面的代碼變為:

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需要在lambda中使用try / catch塊。 在我公司,我們長時間使用兩個接口:

  • IOut<T>等同於java.lang.functions.Factory ;
  • 通常需要檢查異常傳播的情況的特殊接口: interface IUnsafeOut<T, E extends Throwable> { T out() throws E; } interface IUnsafeOut<T, E extends Throwable> { T out() throws E; }

在遷移到Java 8期間,應該刪除IOut<T>IUnsafeOut<T> ,但是IUnsafeOut<T, E>沒有完全匹配。 如果lambda表達式可以處理未經檢查的已檢查異常,則可以像上面的構造函數中的以下內容一樣使用:

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

看起來更清潔。 我看到我可以重寫ObjectPool超類來接受我們的IUnsafeOut<T> ,但據我所知,Java 8還沒有完成,所以可能會有一些變化,如:

  • 實現類似於IUnsafeOut<T, E> ? (老實說,我認為這很髒 - 主題必須選擇接受的內容: Factory或“不安全工廠”,不能兼容方法簽名)
  • 簡單地忽略lambda中的已檢查異常,所以在IUnsafeOut<T, E>代理中不需要嗎? (為什麼不呢?例如,另一個重要的變化:我使用的OpenJDK, javac現在不需要將變量和參數聲明為final在匿名類[功能接口]或lambda表達式中捕獲)

所以問題通常是:有沒有辦法繞過lambdas中的已檢查異常,或者是否計劃將來Java Java最終發布?

更新1

嗯-mm,據我所知,我們目前所擁有的,目前似乎沒有辦法,儘管參考文章的日期是2010年: Brian Goetz解釋了Java中的異常透明度 。 如果在Java 8中沒有什麼變化,這可以被認為是一個答案。 Brian也說interface ExceptionalCallable<V, E extends Exception> (我提到的IUnsafeOut<T, E extends Throwable>我們的代碼遺產)幾乎沒用,我同意他的看法。

我還想念別的嗎?




您是否考慮過使用RuntimeException(未經檢查)的包裝類將原始異常從lambda表達式中走私出來,然後將包裝的異常強制轉換原始的已檢查異常?

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();
    }
}

創建自己的唯一類應該可以防止出現在lambda中包含的異常的另一個未經檢查的異常的可能性,即使在捕獲並重新拋出異常之前將異常序列化在管道中的某個位置也是如此。

嗯......我唯一看到的問題是你在構造函數中執行此操作,調用super(),根據法律,它必須是構造函數中的第一個語句。 try算作以前的陳述嗎? 我在自己的代碼中有這個工作(沒有構造函數)。




以所描述的方式包裝異常不起作用。 我嘗試了它,我仍然得到編譯器錯誤,這實際上是根據規範:lambda表達式拋出異常,它與方法參數的目標類型不兼容:Callable; call()不拋出它所以我不能將lambda表達式作為Callable傳遞。

所以基本上沒有解決方案:我們堅持編寫樣板文件。 我們唯一能做的就是表達我們的意見,這需要修復。 我認為規範不應該盲目地丟棄基於不兼容的拋出異常的目標類型:它應該隨後檢查拋出的不兼容異常是否被捕獲或聲明為調用範圍中的拋出。 對於沒有內聯的lambda表達式,我建議我們可以將它們標記為靜默拋出已檢查的異常(在編譯器不應檢查的意義上是靜默的,但運行時仍然應該捕獲)。 讓我們用=>標記那些 - > - >我知道這不是一個討論網站,但由於這是問題的唯一解決方案,讓自己聽到,讓我們改變這個規範!




可以拋棄你的lambdas,它們只需要用你自己的方式聲明(不幸的是,這使得它們不能在標準的JDK代碼中重複使用,但是,嘿,我們盡我們所能)。

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

或者更通用的版本:

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

here 。 還提到使用“sneakyThrow”來聲明已檢查的異常,但仍然拋出它們。 這有點傷害我的頭腦,也許是一種選擇。




Related