これに似た2つの関連メソッドを持つインターフェースを設計しています。
public interface ThingComputer {
default Thing computeFirstThing() {
return computeAllThings().get(0);
}
default List<Thing> computeAllThings() {
return ImmutableList.of(computeFirstThing());
}
}
実装の約半分は1つのことしか計算しませんが、残りの半分はもっと計算する可能性があります。
これは、広く使用されているJava 8コードで前例がありますか?Haskellがいくつかのタイプクラス(たとえば、Eq
)で同様のことを行うことを知っています。
良い点は、2つの抽象クラス(SingleThingComputer
とMultipleThingComputer
)がある場合よりも大幅に少ないコードを記述する必要があることです。
欠点は、空の実装はコンパイルされますが、実行時にStackOverflowError
で爆破されます。 ThreadLocal
を使用して相互再帰を検出し、より適切なエラーを生成することは可能ですが、バグのないコードにオーバーヘッドが追加されます。
TL; DR:これを行わないでください。
ここに示すのは、もろいコードです。
インターフェースは契約です。 「取得するオブジェクトに関係なく、XとYを実行できます」と書かれています。書かれているように、あなたのインターフェースはスタックオーバーフローを引き起こすことが保証されているため、XもYもX(Y)も行いません。
Error またはサブクラスをスローすると、キャッチできない重大なエラーが発生します。
Errorは、Throwableのサブクラスであり、妥当なアプリケーションがキャッチしようとしてはならない重大な問題を示します。
さらに、 VirtualMachineError は StackOverflowError の親クラスであり、次のように述べています。
Java仮想マシンが壊れているか、仮想マシンが動作を継続するために必要なリソースを使い果たしたことを示すためにスローされます。
プログラムはJVMリソースに関係するべきではありません。それがJVMの仕事です。通常の操作の一部としてJVMエラーを引き起こすプログラムを作成することは悪いことです。これは、プログラムがクラッシュすることを保証するか、このインターフェイスのユーザーに、関係のないエラーをトラップするように強制します。
あなたは Eric Lippert を「C#言語設計委員会のメンバー」という名誉のような努力から知っているかもしれません。彼は人々を成功または失敗に追いやる言語機能について語っています。これは言語機能でも言語設計の一部でもありませんが、インターフェースの実装やオブジェクトの使用に関しても同様に彼の主張は有効です。
ウェストリーが目を覚ますと、悲鳴を上げるアルビノと不吉な6本指の男、ルーゲン伯爵と一緒に絶望の穴に閉じ込められていることに気づいたプリンセスブライドで覚えていますか?絶望の落とし穴の基本的な考え方は2つあります。第一に、それは落とし穴なので、陥りやすいが登りにくい作業です。そして第二に、それは絶望を誘発します。したがって、名前。
インターフェイスがデフォルトでStackOverflowError
をスローすると、開発者はPit of Despairに押し込まれ、悪い考えです。代わりに、開発者を 成功の秘訣 に向けて推進します。 easyにして、JVMをクラッシュさせずにインターフェースを正しく使用します。
メソッド間の委任はここで問題ありません。ただし、依存関係は一方向に進む必要があります。 有向グラフ のようなメソッド委譲について考えたい。各メソッドはグラフ上のノードです。メソッドが別のメソッドを呼び出すたびに、呼び出し側のメソッドから呼び出されたメソッドにEdgeを描画します。
グラフを描いて周期的であることに気づいたら、それはコードのにおいです。これは、絶望の落とし穴に開発者を押し込む可能性があります。これは断固として禁止されるべきではないことに注意してください。1つだけ注意を使用する必要があります。再帰的アルゴリズムは、具体的には呼び出しグラフにサイクルがあります。それは問題ありません。それを文書化し、開発者に警告します。再帰的でない場合は、そのサイクルを壊してみてください。それができない場合は、スタックオーバーフローを引き起こす可能性のある入力を見つけ、それを軽減するか、他に何も機能しない場合の最後のケースとして文書化します。