「プロデューサー」インターフェースを作成しました(メソッド参照で使用するため、それぞれ単体テスト用に簡単にモック化します):
@FunctionalInterface
public interface Factory<R, T, X extends Throwable> {
public R newInstanceFor(T t) throws X;
}
私の最初のユースケースは実際にいくつかのチェック済みWhateverException
をスローする必要があったので、私はそのように作成しました。
しかし、私の2番目の使用例では、スローするXがありません。
コンパイラーを幸せにするために思いつくことができる最高のものは次のとおりです。
Factory<SomeResultClass, SomeParameterClass, RuntimeException> factory;
それはコンパイルされ、私が必要とすることを行いますが、それでも醜いです。その単一のインターフェースを保持する方法はありますが、特定のインスタンスを宣言するときにXを提供しませんか?
それを行う唯一の方法はサブクラス化です-しかし、あなたはそれを知っていたと思います。私の議論をより強くするために、BinaryOperator
を拡張するBiFunction
を見てください。
Javaではできません。唯一の方法は、サブインターフェイスを作成することです。
public interface DefaultExceptionFactory<R, T>
extends Factory<R, T, RuntimeException>
これはより「ソーシャルエンジニアリング」の答えです。ラムダフォームに何もスローしないという契約を置きます。
public interface Factory<T, R, X> {
public R newInstanceFor(T arg) throws X;
public static Factory<R, U, AssertionError> neverThrows(Factory<U, V, ?> input) {
return u -> {
try {
return input.newInstanceFor(u);
}
catch(Throwable t) {
throw new AssertionError("Broken contract: exception thrown", t);
}
};
}
}
使用法は次のようになります。
class MyClass {
Factory<MyInput, MyOtherClass, AssertionError> factory;
MyClass(Factory<MyInput, MyOtherClass, ?> factory) {
this.factory = Factory.neverThrows(factory);
}
public void do() {
factory.newInstanceFor(new MyInput()).do();
}
}
このアプローチの欠点:タイプシグネチャでコントラクトを実際に指定することはできません。コントラクトは実装の詳細になります。これを型シグネチャに含める場合は、2番目のサブインターフェイスが必要になります。
可能であれば、以下のコードのようなジェネリックとしてメソッドを定義できます。
@FunctionalInterface
public interface Factory<R, T> {
public <X extends Throwable> R newInstanceFor(T t) throws X;
}
Project Lombokの @ SneakyThrows アノテーションを使用できます:
@FunctionalInterface
public interface Factory<R, T> {
@SneakyThrows
R newInstanceFor(T t);
}
これにより、任意の例外(チェックありまたはチェックなし)をスローできます。ただし、この機能は慎重に扱う必要があるため、ドキュメントをお読みください。