
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