Optionalクラスは、 これとHabréのこれを含む多くの記事とチュートリアルに専念しています。
それらのほとんどは、このクラスのメソッドがどのように呼び出されるかを示します。 このチュートリアルでは、 なぜ、なぜ、どの場合に1つまたは別のクラスメソッドを適用することが可能(または必要な場合でも)に焦点を当てます。 これは非常に重要だと思います。なぜなら、このチュートリアルの最初の記事の後に調査が示したように、すべてのJavaプログラマーがこのクラスのメソッドの能力をフルに活用することに興味を持ったわけではないからです。
クラスメソッドのより良い説明のために、他のほとんどのチュートリアル(コーヒーメーカー、フィルターユニット、ミキサーなど)よりも複雑で直感的な例を使用します。
これは、動的構造を持つオブジェクトを処理する場合のOptionalクラスの使用に関するシリーズの2番目の記事です。 最初の記事では、Optionalを使用できない、または使用したくない状況でNullPointerExceptionを回避する方法について説明しました。
この記事では、Java 8が提供するすべてのクラスメソッドを取り上げ、Java 9のクラス拡張については、このシリーズの3番目の記事で説明します。 4番目の記事は 、このクラスに必要な(著者の観点からの)追加について説明します。
5番目の記事では、クラス内でOptionalを使用する場所について説明し、要約を読んで、シリーズを最後まで読んだすべての読者に貴重な贈り物を贈ります。
このチュートリアルには、Junitテストを含む多くのソースコードが含まれます。 私は、テストコードを読むことでより良い学習に役立つという私の仲間のライターの意見を共有しています。 私のプロジェクトのすべてのソーステキストはGitHubにあります 。
そのため、 このシリーズの最初の記事では、動的構造を持つオブジェクトを実装するときに使用できるアプローチを検討し、この状況でOptionalが他のアプローチよりも常にうまく機能する理由を正当化することを約束しました。 私たちは約束を果たし始めます。 定義から始めましょう。
オプトナールとは何ですか?
特定の例を検討する前に、「オプションは何ですか?」という質問に答えようとします。
私自身の明確な定義を与えるリスクがあります。 最初の近似では、Optionalは、眼鏡などの物理的な物体の場合のソフトウェア類似物です。 オブジェクトがケース内にあるかどうかは、isPresent()メソッドを使用して確認できます。 そこにある場合は、get()メソッドを使用して取得できます。 一般的に、シリーズのタイトル画像にほぼ示されています。
だから:
最初の近似では、オプションはあるオブジェクトの場合です。
最小限の使用
最初の例では、Java 8 Optionalを使用して、飲料水タップとボイラーを組み合わせたデバイスの動作をシミュレートしようとしています。
そのスキームを以下の図に示します。
ご覧のとおり、デバイスが機能するには水と電気が必要です。 出口では、生水または沸騰した水を分配できます。
したがって、このデバイスの入力は、次のようなインターフェースで記述できます。
public interface IBoilerInput { void setAvailability(boolean waterAvailable, boolean powerAvailable); }
出力は次のようになります。
public interface IBoilerOutput { Optional<CupOfWater> getCupOfWater(); Optional<CupOfBoiledWater> getCupOfBoiledWater(); }
(入力データに応じて)デバイスは生の水と沸騰した水を提供する場合としない場合があるため、get ... using Optionalを呼び出した結果を示します。
デバイス全体の動作は、入力メソッドと出力メソッドを組み合わせたインターフェースを表します。
public interface IBoiler extends IBoilerInput, IBoilerOutput {}
さまざまな水を表すクラスは、私たちにとってあまり関心がないので、最小限の方法で実装します。 原水の提供のためのクラスは次のとおりです。
public class CupOfWater { public CupOfBoiledWater boil() {return new CupOfBoiledWater();} }
沸騰した水の一部は、原水とは異なる新しいオブジェクトであると想定します。 したがって、独立したクラスとして提示します。
public class CupOfBoiledWater {}
これで、タスクが設定されました。 TDD(Test-Driven Development)の最良の伝統では、最初に簡単なデバイスの動作を正しくシミュレートしたかどうかを確認するテストを作成します。
JUnitテストBoiler1Test
public class Boiler1Test { private IBoiler boiler; @Before public void setUp() throws Exception { boiler = new Boiler1(); } @Test public void testBothNotAvailable() { boiler.setAvailability(false, false); assertFalse(boiler.getCupOfWater().isPresent()); assertFalse(boiler.getCupOfBoiledWater().isPresent()); } @Test public void testPowerAvailable() { boiler.setAvailability(false, true); assertFalse(boiler.getCupOfWater().isPresent()); assertFalse(boiler.getCupOfBoiledWater().isPresent()); } @Test public void testWaterAvailable() { boiler.setAvailability(true, false); assertTrue(boiler.getCupOfWater().isPresent()); assertFalse(boiler.getCupOfBoiledWater().isPresent()); } @Test public void testBothAvailable() { boiler.setAvailability(true, true); assertTrue(boiler.getCupOfWater().isPresent()); assertTrue(boiler.getCupOfBoiledWater().isPresent()); } }
このテストでは、電気の可用性に関係なく、入口に水が供給されると、アプライアンスが実際に原水を供給することを確認します。 しかし、このデバイスは、水と電気の両方が存在する場合にのみ沸騰した水を与えます。
実装に進む前に、シリーズの最初の記事で説明されているアプローチの一部として、この問題の解決策をどのようにプログラムするかを少しの間考え、頭の中で、またはキーボードの後ろで考えてください。
has ... get ...ペアの使用
配列または値のシートを返すことにより
外部に排出された製品の活動の兆候を使用する。
あなたが本当にそれを想像しようとし、さらに良いことに、これらのアプローチの中で問題の解決策をプログラムしようとすると、Java 8 Optionalが私たちのプログラミング生活にもたらすシンプルさと優雅さを確かに評価するでしょう。
私の、おそらく最良の解決策を見てください:
public class Boiler1 implements IBoiler { private boolean waterAvailable; private boolean powerAvailable; @Override public void setAvailability(boolean waterAvailable, boolean powerAvailable) { this.waterAvailable = waterAvailable; this.powerAvailable = powerAvailable; } @Override public Optional<CupOfWater> getCupOfWater() { return waterAvailable ? Optional.of(new CupOfWater()) : Optional.empty(); } @Override public Optional<CupOfBoiledWater> getCupOfBoiledWater() { if(!powerAvailable)return Optional.empty(); return getCupOfWater().map(cupOfWater->cupOfWater.boil()); } }
リストの最後の行に注意してください。ここでは、Optionalクラスのmap()メソッドが使用されます。 これにより、処理チェーンを構築できます。 チェーン内のリンクのいずれかで、それ以上の処理が不可能であることが判明した場合、チェーン全体が空の回答を返します。
ボイラーモデルの結果は、ブール変数を使用して設定された外部条件に依存します。 しかし、実際に最も興味深いタスクでは、単純な変数ではなく、オブジェクトが入力に供給されます。 nullになる可能性があるものを含む。
動作がブール変数ではなく、ゼロ値を許可する「旧モード」オブジェクトによって決定される場合、Optionalをどのように適用できるかを考えてみましょう。
ボイラーの入力を、最初の例とはわずかに異なるモデルとし、次のように定義します。
public interface IBoilerInput2 { void setAvailability(@Nullable CupOfWater water, boolean powerAvailable); }
水オブジェクトのゼロ値は、給水から水がデバイスに入らないことを意味します。
次に、デバイス全体の動作は、次のインターフェイスによって定義されます。
public interface IBoiler2 extends IBoilerInput2, IBoilerOutput {}
前の例のように、実装の正確性を検証するテストを定義します。
JUnitテストBoiler2Test
public class Boiler2Test { private IBoiler2 boiler; @Before public void setUp() throws Exception { boiler = new Boiler2(); } @Test public void testBothNotAvailable() { boiler.setAvailability(null, false); assertFalse(boiler.getCupOfWater().isPresent()); assertFalse(boiler.getCupOfBoiledWater().isPresent()); } @Test public void testPowerAvailable() { boiler.setAvailability(null, true); assertFalse(boiler.getCupOfWater().isPresent()); assertFalse(boiler.getCupOfBoiledWater().isPresent()); } @Test public void testWaterAvailable() { boiler.setAvailability(new CupOfWater(), false); assertTrue(boiler.getCupOfWater().isPresent()); assertFalse(boiler.getCupOfBoiledWater().isPresent()); } @Test public void testBothAvailable() { boiler.setAvailability(new CupOfWater(), true); assertTrue(boiler.getCupOfWater().isPresent()); assertTrue(boiler.getCupOfBoiledWater().isPresent()); } }
これらのテストを最初のモデルのボイラーのテストと比較すると、非常に類似していることがわかります。 異なるセットから同じテストの結果を確認することは同じです。 さて、入力は、水源に対してtrueの代わりにオブジェクトをフィードし、false -nullの代わりに異なる点で異なります。
実装自体は次のとおりです。
public class Boiler2 implements IBoiler2 { @Nullable private CupOfWater water; private boolean powerAvailable; @Override public void setAvailability(@Nullable CupOfWater water, boolean powerAvailable) { this.water = water; this.powerAvailable = powerAvailable; } @Override public Optional<CupOfWater> getCupOfWater() { return Optional.ofNullable(water); } @Override public Optional<CupOfBoiledWater> getCupOfBoiledWater() { if(!powerAvailable)return Optional.empty(); return getCupOfWater().map(cupOfWater->cupOfWater.boil()); } }
ご覧のとおり、Optional.ofNullable()メソッドを使用すると、ゼロの可能性がある値を持つ危険なオブジェクトをエレガントに「置く」ことができます。 オブジェクトがnullの場合、ケースは空になります。 それ以外の場合は、必要なオブジェクトが含まれています。
オプションの最小限の使用のために、最初の結果を取得し、最初のルールを策定します。
メソッドが、存在する場合と存在しない場合があるオブジェクトを返す場合、それをOptionalに「入れ」ます。 敷設するときは、次のルールを使用します。
状態 使用されるクラスメソッド オブジェクトがありません Optional.empty() オブジェクトが存在し、間違いなくnullではない Optional.of(...) オブジェクトは存在しますが、nullの場合があります Optional.ofNullable(...)
オブジェクトがケース内にあるかどうかは、isPresent()メソッドを使用して決定します。 チェックで肯定的な結果が得られた場合は、get()を使用してケースからオブジェクトを取得します
したがって、戻り値としてnullを使用しないようにOptionalの使用をマスターしました。
しかし、私たちはそこで止まりません。
ここで、特定のリソースがmain要素とreserve要素で表される場合の、それほどまれではない別の状況を考えてみましょう。
さて、巣の卵があると......
Stashは、バックアップリソースの一般的な定義です。 この用語の感情的な側面から脱線し、問題の技術的な側面を考えてみましょう。
技術システムでは、多くの場合、同じタイプのリソースが複数の方法で利用可能です。
次の例では、単純な給水器具を検討します。 したがって、夏の住民が使用する灌漑装置が配置されます。 雨水は特別な容器に集められ、最初に消費されます。 そこにない場合や終わった場合、給水からの水が消費されます。
雨タンクの不完全な充填とそのサイズに関する不必要な詳細でタスクを複雑にすることはなく、単純に使い慣れたCupOfWaterクラスを再度使用します。
このようなデバイスの入力は次のように説明されます。
public interface IWaterDispenserInput { void setAvailability(@Nullable CupOfWater firstPortion); }
雨水が収集されない場合、入り口にはゼロのオブジェクトがあり、そうでない場合は通常のオブジェクトです。
デバイスの出力は、次のインターフェースによって記述されます。
public interface IWaterDispenserOutput { CupOfWater getCupOfWater(); }
出力には、CupOfWaterオブジェクトがあり、オプションではないことに注意してください。 これは、興味のあるメカニズムをより明確に示すためです。 親愛なる読者が理解すれば、サンプルを簡単に再プログラムして、出力としてOptionalを取得できます。
デバイス全体の動作は、次のインターフェイスの組み合わせによって決まります。
public interface IWaterDispenser extends IWaterDispenserInput, IWaterDispenserOutput {}
前の例のように、最初にテストを準備して実装の動作をテストします。
JUnitテストWaterDispenser1Test
public class WaterDispenser1Test { private IWaterDispenser waterDispenser; @Before public void setUp() throws Exception { waterDispenser = new WaterDispenser1(); } @Test public void testMainAvailable() { waterDispenser.setAvailability(new CupOfWater()); assertNotNull(waterDispenser.getCupOfWater()); } @Test public void testMainNotAvailable() { waterDispenser.setAvailability(null); assertNotNull(waterDispenser.getCupOfWater()); } }
雨水の入ったタンクが満杯かどうかに関係なく、デバイスは水を供給します。後者の場合、水は「予備」(給水)から取られるためです。
実装を検討してください。
public class WaterDispenser1 implements IWaterDispenser{ @Nullable private CupOfWater mainCup; @Override public void setAvailability(@Nullable CupOfWater mainCup) { this.mainCup = mainCup; } @Override public CupOfWater getCupOfWater() { return Optional.ofNullable(mainCup).orElse(new CupOfWater()); } }
ご覧のとおり、orElseメソッドがofNullable()メソッドに追加されています。 最初の要素が空のOptional(雨水が蓄積していない)を返す場合、2番目のメソッドはそれ自体からオブジェクトを追加します。 最初の方法が空でないOptionalを生成する場合、2番目の方法は単純にそれを通過させ、水道水はそのまま残ります。
この実装では、バックアップ機能を想定しています。 この前にオブジェクトを作成する必要がある場合(この場合、ポンプ水)、サプライヤータイプのパラメーターでorElseGet()メソッドを使用できます。
public class WaterDispenser2 implements IWaterDispenser{ @Nullable private CupOfWater mainCup; @Override public void setAvailability(@Nullable CupOfWater mainCup) { this.mainCup = mainCup; } @Override public CupOfWater getCupOfWater() { return Optional.ofNullable(mainCup).orElseGet(()->new CupOfWater()); } }
ジンをビンから出さないでください
場合によっては、APIの制限により、戻り値としてOptionalを使用できません。
クライアントが常に関数の出力で何らかのオブジェクトを期待するように、インターフェイスが定義されているとします。 リクエスト時にリクエストされたリソースがなく、nullを返したくない場合、例外をスローする方法は1つしかありません。 したがって、魔神をボトルから出さないようにします。リリースされたnullオブジェクトに、クライアントコードNullPoiner Exceptionで既に振り向く機会を与えません。
この場合、Java 8 Optionalは役立ちますか? はい、できます。
しかし、ソリューションを検討する前に、その作業の正確性をチェックするテストを準備します。
@Test (expected = IllegalStateException.class) public void testMainNotAvailable() { waterDispenser.setAvailability(null); waterDispenser.getCupOfWater(); fail("This code line must be not reached"); }
そして、ここに解決策があります:
public class WaterDispenser3 implements IWaterDispenser{ @Nullable private CupOfWater mainCup; @Override public void setAvailability(@Nullable CupOfWater mainCup) { this.mainCup = mainCup; } @Override public CupOfWater getCupOfWater() { return Optional.ofNullable(mainCup).orElseThrow(()->new IllegalStateException("Resource not available")); } }
この決定は多くの読者を納得させるものではないと思います。 実際、ifでnullをチェックするよりも優れているのは何ですか?
このソリューションを支持する主な論点は、この方法で関数呼び出しのチェーンを構築する能力です。 ただし、例外によってチェーンが中断される場合があります。 このシリーズの4回目の記事では、このような関数呼び出しのチェーンでの例外処理の問題に対する独自の解決策を提案するリスクがあります。
動的オブジェクトを作成するためのいくつかの選択肢がある場合に、オプションを使用するための新しいルールグループを策定するときが来ました。
動的オブジェクトを作成するための選択肢が2つ以上ある場合は、次の規則を使用します。
状態 使用されるクラスメソッド 代替オブジェクトが存在する orElse(...) 最初に代替オブジェクトを作成する必要があります(たとえば、リポジトリから取得する) orElseGet(()-> ...) 代替リソースがなくなった(例外をスロー) orElseThrow(()->新しいIllegalStateException(...))
これまで、動的構造を持つオブジェクトの作成および単純な使用の段階でOptionalの使用を検討してきました。 ここで、Optionalがそのようなオブジェクトの変換にどのように役立つかという問題を検討します。
コンバーターでのオプションの使用
Transformerは、入力でオブジェクトを受け取り、それを変更するか、他のオブジェクトに変換します。 この場合、Optionalの使用に制限しているため、入力オブジェクトとして常にOptionalがあります。 これは、オブジェクトが存在する、または存在しないケースまたはコンテナとして想像できることを思い出してください。
任意のタイプの「実際の」オブジェクトに変換することも、新しいオブジェクトを使用して新しいケースに変換することもできます。
抽象レベルでは、このような変換のすべてのバリアントは、以下の3つの式の形式で表現できます。
T t = f1(オプションの<T> opt)
U u = f2(オプションの<T> opt)
オプション<U> = f3(オプション<T> opt)
変換関数f1、f2、f3の役割の候補-Optionalクラスのメソッドを次の表に示します。
f1の役割の候補 f2の役割の候補 f3の役割の候補 フィルター() 地図() flatMap() またはElse() 地図() orElseGet()
このシリーズの以前の投稿では、これらの方法のほとんどをすでに取り上げました。 フィルターとflatMapのみが未検査のままでした。
以下では、これらのメソッドの使用例を見ていきます。
フィルタリング(フィルターメソッドを使用)
次の例では、filter()メソッドの使用を検討します。このメソッドは、ケースが空ではなく、その中に含まれるオブジェクトがある基準を満たす場合にのみオブジェクトを返します。
この場合、オブジェクトとして、タンク内の水の一部を使用して、夏のコテージの散水を収集します。 物理的および化学的特徴の分析に入ることなく、収集された水はきれいである(基準を満たす)かそうでないかを想定しています。
デバイスの最も簡略化された図を下の図に示します。
デバイスの動作を可能な限り単純化して、すべてを問題に減らします。特定の場合に分配される水の一部であるかどうか。 この単純化の後、デバイスの動作のセマンティクスは次の表で説明できます。
この例の完全なコードは、パッケージeu.sirotin.example.optional4の記事の冒頭に記載されているGitHuBプロジェクトにあります。
最初に、収集された雨水を表すクラスを理解します。
public class RainWater { private final boolean clean; public RainWater(boolean clean) { this.clean = clean; } public boolean isClean() { return clean; } }
ご覧のとおり、isClean()メソッドを使用して、収集した水がきれいかどうかを確認できます。
このクラスは、デバイスの入力パラメーターとして使用されます。
「ケース」内の同じオブジェクトがデバイスの出力で使用されます。
public interface IRainWaterDispenserInput { void setAvailability(@Nullable RainWater rainWater); }
public interface IRainWaterDispenserOutput { Optional<RainWater> getRainWater(); }
そして、デバイスの動作は完全に複合インターフェースによって記述されます:
public interface IRainWaterDispenser extends IRainWaterDispenserInput, IRainWaterDispenserOutput {}
繰り返しになりますが、最初にテストを準備して、デバイスの動作のモデリングの正確性を検証します。 以下のテストで期待されることは、上記の動作表と完全に一致していることは簡単にわかります。
JUnitテストRainWaterDispenser1Test
public class RainWaterDispenser1Test { private IRainWaterDispenser rainWaterDispenser; @Before public void setUp() throws Exception { rainWaterDispenser = new RainWaterDispenser1(); } @Test public void testRainWaterAvailableAndClean() { rainWaterDispenser.setAvailability(new RainWater(true)); assertTrue(rainWaterDispenser.getRainWater().isPresent()); assertTrue(rainWaterDispenser.getRainWater().get().isClean()); } @Test public void testWaterNotAvailable() { rainWaterDispenser.setAvailability(null); assertFalse(rainWaterDispenser.getRainWater().isPresent()); } @Test public void testRainWaterAvailableNotClean() { rainWaterDispenser.setAvailability(new RainWater(false)); assertFalse(rainWaterDispenser.getRainWater().isPresent()); } }
それでは、Optionalを使用してクラスの実装に取り掛かりましょう。
全文は次のとおりです。
public class RainWaterDispenser implements IRainWaterDispenser{ @Nullable private RainWater rainWater; @Override public void setAvailability(@Nullable RainWater rainWater) { this.rainWater = rainWater; } @Override public Optional<RainWater> getRainWater() { return Optional.ofNullable(rainWater).filter(RainWater::isClean); } }
最後の行は、filter()メソッドの使用を示しています。 isClean()オブジェクトメソッドによって返される値は、基準として使用されます。
また、コールチェーンでのofNullable()およびfilter()メソッドの使用にも注意してください。 とてもエレガントに見えませんか?
変換-(flatMapメソッドを使用)
前の例で検討したデバイスが、汚染された雨水を洗浄できる別のデバイスに交換されたとします。
最も簡単なスキームを以下に示します。
そして、デバイスの動作は、このようなセマンティックテーブルによって記述されます。
これと前の表を比較すると、新しいデバイスの明らかな利点がわかります。汚染された雨水が入ってもきれいな水が出ます。
いつものように、デバイスの入力と出力を記述するインターフェイスから始めます。
public interface IRainWaterCleanerInput { void setAvailability(@Nullable RainWater rainWater); }
public interface IRainWaterCleanerOutput { Optional<CupOfWater> getCleanedWater(); }
デバイスが期待される動作を実装しているかどうかを確認するテストを準備します。
JUnitテストRainWaterCleanerTest
public class RainWaterCleanerTest { private IRainWaterCleaner rainWaterDispenser; @Before public void setUp() throws Exception { rainWaterDispenser = new RainWaterCleaner(); } @Test public void testRainWaterAvailableAndClean() { rainWaterDispenser.setAvailability(new RainWater(true)); assertTrue(rainWaterDispenser.getCleanedWater().isPresent()); } @Test public void testWaterNotAvailable() { rainWaterDispenser.setAvailability(null); assertFalse(rainWaterDispenser.getCleanedWater().isPresent()); } @Test public void testRainWaterAvailableNotClean() { rainWaterDispenser.setAvailability(new RainWater(false)); assertTrue(rainWaterDispenser.getCleanedWater().isPresent()); } }
さて、クラス自体を考えてみましょう:
public class RainWaterCleaner implements IRainWaterCleaner { @Nullable private RainWater rainWater; @Override public void setAvailability(@Nullable RainWater rainWater) { this.rainWater = rainWater; } @Override public Optional<CupOfWater> getCleanedWater() { return Optional.ofNullable(rainWater).flatMap(w->Optional.of(new CupOfWater())); } }
flatMap()メソッドの使用は、最後の行に示されています。 map()メソッドとは異なり、このメソッドはオブジェクト自体を返すのではなく、空のケース(コンテナ)を返します。
コンシューマーオブジェクトでのオプションの使用
最初の例では、isPresent()メソッドの使用を検討しました。これにより、オブジェクトがケース内にあるかどうかを判別できます。 isPresent(...)の代わりに、利用可能な場合にのみさらなる処理が想定される場合、ifPresent(...)を使用する方が便利です。
このメソッドは値を返しませんが、ケースが存在する場合、ケース内のオブジェクトを処理できます。 彼がそこにいなければ、何も起こりません。
別のデバイスの例でのアクションを検討してください。これは、追加機能のために前のデバイスの複雑さです。 このデバイスの新しいバージョンは、雨水を汚染から取り除くだけでなく、特定の添加剤と混合することもできます。 前の例のように、これらの添加物の詳細には興味がありません。
デバイス図を次の図に示します。
まず、混合の結果を表す新しいクラスを定義します。
public class MixedWater extends CupOfWater { public MixedWater(CupOfWater water) {} }
デバイスの出力は、このインターフェイスによって決定されます。
public interface IMixerOutput extends IRainWaterCleanerOutput { Optional<MixedWater> getMixedWater(); }
入力として、前の例のインターフェイスを使用します。 次に、デバイスの完全な入力と出力は、このようなジョイントインターフェイスによって決定されます。
public interface IMixer extends IRainWaterCleanerInput, IMixerOutput {}
デバイスの動作は以前のデバイスの動作に似ていますが、浄化された雨水の代わりに、目的の添加剤を含む浄化された雨水が得られます。
デバイスの動作が正しいことを確認するテストを行ってみましょう。
JUnitテストMixerTest
public class MixerTest { private IMixer mixer; @Before public void setUp() throws Exception { mixer = new Mixer(); } @Test public void testRainWaterAvailableAndClean() { mixer.setAvailability(new RainWater(true)); assertTrue(mixer.getMixedWater().isPresent()); } @Test public void testWaterNotAvailable() { mixer.setAvailability(null); assertFalse(mixer.getMixedWater().isPresent()); } @Test public void testRainWaterAvailableNotClean() { mixer.setAvailability(new RainWater(false)); assertTrue(mixer.getMixedWater().isPresent()); } }
そして、メインクラスの実装は次のとおりです。
public class Mixer extends RainWaterCleaner implements IMixer{ private MixedWater result = null; @Override public Optional<MixedWater> getMixedWater() { super.getCleanedWater().ifPresent(this::mix); return Optional.ofNullable(result); } private void mix(CupOfWater water) { result = new MixedWater(water); } }
ifPresent()メソッドの使用を詳しく見てみましょう。ご覧のとおり、mix()クラスのメソッドがメソッドの入力パラメーターとして使用されます。彼は、入力パラメーターとしてCupOfWaterタイプのオブジェクトを期待しています。この特定のタイプのオブジェクトを持つケースは、getCleanedWater()メソッドを返すことに注意してください。
コンシューマ(クライアント)でOptionalを使用するためのルールを策定します。
空の可能性のあるオブジェクトの処理が正の場合のみ実行される場合(オブジェクトは空ではありません)-IfPresent(...)メソッドを使用します;
そうでない場合、isPresent()メソッドを使用してオブジェクトがケース内にあるかどうかを確認できます。そこにある場合は、get()メソッドを使用して取得できます。
これが、Java 8のOptionalクラスに関連して検討したかったすべての例です。
しかし、このクラスに関する議論はまだ終わっていません。次の記事では、Java 9のこのクラスの革新、およびその欠点と制限について説明します。このシリーズの3番目の記事に
移行します。
イラスト: ThePixelman