69

私は Java 8 の新しいラムダ機能をいじっていて、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);
    }

}

関数型インターフェースをラムダ式に変換すると、上記のコードは次のようになります。

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が必要です。私の会社では、長い間 2 つのインターフェイスを使用しています。trycatch

  • IOut<T>java.lang.functions.Factoryこれは;と同等です。
  • 通常はチェック例外の伝播が必要な場合のための特別なインターフェース: interface IUnsafeOut<T, E extends Throwable> { T out() throws E; }.

IOut<T>Java 8 への移行中にとの両方IUnsafeOut<T>が削除されるはずですが、 と完全に一致するものはありませんIUnsafeOut<T, E>。ラムダ式がチェックされた例外をチェックされていないかのように処理できる場合、上記のコンストラクターで次のように単純に使用できる可能性があります。

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

それはずっときれいに見えます。ObjectPoolスーパークラスを書き直してIUnsafeOut<T>私たちの

  • に似たものを実装しIUnsafeOut<T, E>ますか?(正直なところ、私はそれを汚いと考えています-サブジェクトは何を受け入れるかを選択する必要があります:どちらか、Factoryまたは互換性のあるメソッドシグネチャを持つことができない「安全でないファクトリ」)
  • ラムダでチェック例外を無視するだけなので、IUnsafeOut<T, E>サロゲートは必要ありませんか? (なぜですか?たとえば、別の重要な変更:私が使用しているOpenJDKは、匿名クラス[関数インターフェイス]またはラムダ式でキャプチャされるjavacように変数とパラメーターを宣言する必要がなくなりました)final

したがって、問題は一般的に次のとおりです。ラムダでチェック済み例外をバイパスする方法はありますか、またはJava 8が最終的にリリースされるまで、将来的に計画されていますか?


更新 1

うーん、私たちが現在持っているものを理解している限り、参照されている記事は 2010 年のものですが、現時点では方法がないようです: Brian Goetz は Java で例外の透過性を説明しています。Java 8 で何も変わっていない場合、これは答えと見なすことができます。また、Brian は、interface ExceptionalCallable<V, E extends Exception>(私が言及しIUnsafeOut<T, E extends Throwable>たコードの遺産から外れたもの) はほとんど役に立たないと言い、私も彼に同意します。

私はまだ何か他のものを見逃していますか?

4

9 に答える 9

49

本当にあなたの質問に答えられるかどうかわかりませんが、単純にそのようなものを使用することはできませんか?

public final class SupplierUtils {
    private SupplierUtils() {
    }

    public static <T> Supplier<T> wrap(Callable<T> callable) {
        return () -> {
            try {
                return callable.call();
            }
            catch (RuntimeException e) {
                throw e;
            }
            catch (Exception e) {
                throw new RuntimeException(e);
            }
        };
    }
}

public class JdbcConnectionPool extends ObjectPool<Connection> {

    public JdbcConnectionPool(int maxConnections, String url) {
        super(SupplierUtils.wrap(() -> DriverManager.getConnection(url)), maxConnections);
    }
}
于 2012-12-26T20:00:40.057 に答える
34

ラムダ メーリング リストでは、これについて徹底的に議論されました。ご覧のように、Brian Goetz が提案した別の方法は、独自のコンビネータを作成することです。

または、独自の簡単なコンビネータを作成することもできます。

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

元の電子メールを書くのにかかった時間よりも短い時間で、一度書くことができます。同様に、使用する SAM の種類ごとに 1 回。

これを代替案ではなく、「ガラスが 99% いっぱい」と見なした方がよいでしょう。すべての問題が解決策として新しい言語機能を必要とするわけではありません。(新しい言語機能は常に新しい問題を引き起こすことは言うまでもありません。)

当時、Consumer インターフェイスは Block と呼ばれていました。

これはJB Nizet's answerに対応していると思います。

後で Brianは、これがこのように設計された理由 (問題の理由) を説明します。

はい、独自の例外的な SAM を提供する必要があります。しかし、ラムダ変換はそれらでうまく機能します。

EG は、この問題に対する追加の言語とライブラリのサポートについて話し合いましたが、最終的に、これはコストと利益のトレードオフが悪いと感じました。

ライブラリベースのソリューションは、SAM タイプ (例外とそうでないもの) で 2 倍の爆発を引き起こします。これは、プリミティブ特殊化のための既存の組み合わせ爆発とうまく相互作用しません。

利用可能な言語ベースのソリューションは、複雑さと価値のトレードオフから敗者でした。代替ソリューションがいくつかありますが、引き続き検討していきますが、明らかに 8 向けではなく、おそらく 9 向けでもありません。

それまでの間、あなたはやりたいことをするためのツールを手に入れました。ラストワンマイルを提供することを望んでいると思います(そして、二次的に、あなたの要求は実際には「チェック済み例外をもうあきらめないでください」という薄いベールに包まれた要求です)が、現在の状態では可能だと思いますあなたはあなたの仕事を成し遂げます。

于 2014-03-27T13:59:03.033 に答える
5

2015 年 9 月:

これにはETを使用できます。ET は、例外変換/翻訳用の小さな Java 8 ライブラリです。

ET を使用すると、次のように記述できます。

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

複数行バージョン:

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

前に行う必要があるのは、新しいExceptionTranslatorインスタンスを作成することだけです。

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

このインスタンスはスレッド セーフであり、複数のコンポーネントで共有できます。必要に応じて、より具体的な例外変換ルール (例: FooCheckedException -> BarRuntimeException) を構成できます。他のルールが利用できない場合、チェックされた例外は自動的に に変換されRuntimeExceptionます。

(免責事項: 私は ET の作成者です)

于 2015-09-02T17:52:58.383 に答える
5

これに役立つ社内プロジェクトを開発しました。上場を決めたのは2ヶ月前。

これが私たちが思いついたものです:

@FunctionalInterface
public interface ThrowingFunction<T,R,E extends Throwable> {
R apply(T arg) throws E;

/**
 * @param <T> type
 * @param <E> checked exception
 * @return a function that accepts one argument and returns it as a value.
 */
static <T, E extends Exception> ThrowingFunction<T, T, E> identity() {
    return t -> t;
}

/**
 * @return a Function that returns the result of the given function as an Optional instance.
 * In case of a failure, empty Optional is returned
 */
static <T, R, E extends Exception> Function<T, Optional<R>> lifted(ThrowingFunction<T, R, E> f) {
    Objects.requireNonNull(f);

    return f.lift();
}

static <T, R, E extends Exception> Function<T, R> unchecked(ThrowingFunction<T, R, E> f) {
    Objects.requireNonNull(f);

    return f.uncheck();
}

default <V> ThrowingFunction<V, R, E> compose(final ThrowingFunction<? super V, ? extends T, E> before) {
    Objects.requireNonNull(before);

    return (V v) -> apply(before.apply(v));
}

default <V> ThrowingFunction<T, V, E> andThen(final ThrowingFunction<? super R, ? extends V, E> after) {
    Objects.requireNonNull(after);

    return (T t) -> after.apply(apply(t));
}

/**
 * @return a Function that returns the result as an Optional instance. In case of a failure, empty Optional is
 * returned
 */
default Function<T, Optional<R>> lift() {
    return t -> {
        try {
            return Optional.of(apply(t));
        } catch (Throwable e) {
            return Optional.empty();
        }
    };
}

/**
 * @return a new Function instance which wraps thrown checked exception instance into a RuntimeException
 */
default Function<T, R> uncheck() {
    return t -> {
        try {
            return apply(t);
        } catch (final Throwable e) {
            throw new WrappedException(e);
        }
    };
}

}

https://github.com/TouK/ThrowingFunction/

于 2016-04-04T05:31:44.803 に答える
4

RuntimeException (チェックされていない) ラッパー クラスを使用してラムダ式から元の例外を密輸し、ラップされた例外を元のチェックされた例外にキャストすることを検討しましたか?

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

独自の一意のクラスを作成すると、例外をキャッチして再スローする前にパイプラインのどこかでシリアル化されている場合でも、ラムダ内でラップした別の未チェックの例外を間違える可能性を防ぐことができます。

うーん...ここで問題と思われる唯一のことは、コンストラクター内でこれを実行し、法律により、コンストラクターの最初のステートメントである必要がある super() を呼び出していることです。try前のステートメントとしてカウントされますか? 私はこれを自分のコードで(コンストラクターなしで)機能させています。

于 2013-08-08T20:16:04.877 に答える
1

説明されている方法で例外をラップしても機能しません。私はそれを試してみましたが、実際には仕様に従っているコンパイラエラーが発生します。ラムダ式は、メソッド引数のターゲットタイプと互換性のない例外をスローします: Callable; call() はそれをスローしないため、ラムダ式を Callable として渡すことはできません。

したがって、基本的に解決策はありません。定型文を書くことに行き詰まっています。私たちにできる唯一のことは、これを修正する必要があるという意見を表明することです。仕様は、互換性のないスローされた例外に基づいてターゲット型を盲目的に破棄するべきではないと思います。その後、スローされた互換性のない例外がキャッチされているか、呼び出しスコープでスローとして宣言されているかどうかを確認する必要があります。また、インライン化されていないラムダ式については、チェック済みの例外をサイレントにスローするものとしてマークできることを提案します (コンパイラーがチェックすべきではないという意味でサイレントですが、ランタイムは引き続きキャッチする必要があります)。-> ではなく => でマークしましょう。これがディスカッション サイトではないことはわかっていますが、これが質問に対する唯一の解決策であるため、自分の意見を聞いて、この仕様を変更しましょう!

于 2013-01-13T17:54:06.173 に答える