Java 8およびJava 9のケヌスたたはオプションのオブゞェクトパヌト2「Java 8でそれを行う方法」

ケヌス内のオブゞェクト
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

Source: https://habr.com/ru/post/J347576/


All Articles