Zenn
😀

Javaで関数型にトライ!①

2017/02/16に公開

1.はじめに

java8から関数型インターフェースが提供されるようになりましたが、みなさんのプロダクトではどの程度利用しているでしょうか?
 既に動いているプロダクトの場合、移行が難しい場合も多々あるかと思いますが、実際にプロダクトで利用してみてメリットが多いと感じたので、今回は実際に元の(関数型インターフェースを利用していない)サンプルコードを関数型インターフェースを利用して書き換えてみます。

※本稿ではライブラリJava8 + EclipseCollectionsを使用しています。(下記参照)
EclipseCollections(旧GS Collections)の使い方

2. 類似画像判定メソッドで関数型I/F実践

2.1 処理フロー

下記は例として挙げる類似画像判定メソッドの処理フローになります。
 相違点としてemptyを記載しているとことですが、関数型I/Fを使用しない場合はnullを、使用する場合はOptional.emptyを返却します。

類似画像判定メソッド処理フロー
  画像ハッシュ値でDBから画像を検索
      _____________↓____________
      ↓ある                    ↓なし
   画像データを返却             ↓
                     画像の付加情報から候補を検索
                     ↓ある                  ↓なし
                     ↓                      ↓
          類似画像を検索                ↓
             ________↓_____                空返却
             ↓ある         ↓なし
             ↓            ↓
 類似画像データを返却      空返却

2.2 関数型I/Fを使用しない場合

では上記のフローを関数型I/Fを使用しないで実装してみましょう。下記はコード例です。

関数型I/F未使用
    /**
     * @param imageHash 画像hash値
     * @param addInfo 画像付加情報
     * @return 
     */
    public SimilarImageDto getImage(String imageHash, AddInfo addInfo) {
        //完全一致で検索
        val image = imageRepository.get(imageHash) 

        //存在したらDtoを生成し返却
        if ( image != null ) {
             return SimilarImageDto.create(image);

        //存在しない場合の処理
        } else {
            //付加情報から候補画像を検索
            List<ImageInfo> imgInfoList = imageInfoRepository.find(addInfo);
            //候補から類似判定。存在しない場合nullが返却される。
            if (CollectionUtils.isEmpty(imgInfoList) ) {
                return null;
            }

            for (ImageInfo imgInfo : imgInfoList) {

                //類似画像が存在したらDtoを生成して返却
                if( isSimilar(imgInfo) ){
                    return SimilarImageDto.create(imgInfo);
                }       
            }
            return null;
        }
    }

   //類似画像判定関数
   private boolean isSimilar(ImageInfo imgInfo) = {
       // 類似画像検索処理実施 
        return isSimilar;
    };

2.3 関数型I/Fを使用しない場合

では上記のフローを関数型I/Fを使用して実装してみましょう。下記はコード例です。

関数型I/F使用
    /**
     * @param imgHash 画像hash値
     * @param addInfo 画像付加情報
     * @return 
     */
    public Optional<SimilarImageDto> getImageOpt(String imgHash, AddInfo addInfo) {
        return imageRepository //
          .getOpt(imgHash) //完全一致で検索
          .map(img -> SimilarImageDto.create(img) )//存在したらDtoを生成し返却
          .orElseGet(() -> { //存在しない場合の処理
              return imageInfoRepository.find(addInfo) //付加情報から候補画像を検索
                 .detectOptional( imgInfo -> predicateSimilar.test(imgInfo) )// 候補から類似判定。存在しない場合Optional.emptyが返却される。
                 .map( imgInfo -> SimilarImageDto.create(imgInfo) );//類似画像が存在したらDtoを生成して返却
          });
    }

   //類似画像判定関数
   private Predicate<ImageInfo> predicateSimilar = imgInfo -> {
       // 類似画像検索処理実施 
        return isSimilar;
    };

3. 関数型I/Fを利用してみて

いかがでしたでしょう?
 今回はシンプルなフローでしたが、より複雑になっていくと関数型I/Fを利用しない場合との差異がより顕著になります。
 コード量が短くなることもメリットなんですが、実際に導入してみて感じることは、nullチェックが不要になる、関数を利用することで副作用が減る、スコープが自然と小さくなる、またそれによりテストが簡潔になるなどをメリットとして感じます。

Discussion

ログインするとコメントできます