mike-neckのブログ

Java or Groovy or Swift or Golang

最も正しくないOptionalの使い方

本記事ではJava8から入ったOptionalの最も正しくない使い方を解説します。

なお、これが正しいか正しくないかは読者の方の個人的なセンスにお任せします。


正しくない使い方

入力されたBeanの検証をするときに、if(condition1) { return false; } else if (condition2) { return true; } else { return condition3;}のようなif elseの連鎖を書きたくないために、Optional#filterを使う。

モティベーション

if 〜 else if 〜 else if 〜を書くのが面倒い

お題

次のようなインターフェースを持ったBeanの検証を考えます。

@Managed
public interface PropertyEntry {
    String getName();
    void setName(String name);

    Type getType();
    void setType(Type type);

    String getRefType();
    void setRefType();
}

enum Type {
    STRING("String", false), INTEGER("Integer", false), SET("Set", true), REF("", true);
    Type(String typeName, boolean refToAnotherType) {/*constructor 省略*/}
    // getter 省略
}

さて、このBeanですが、次のようなValidationを行います。

  • PropertyEntryオブジェクトがnullの場合はfalse
  • namenullまたは空文字の場合はfalse
  • nameがLower Camel Caseでない場合はfalse
  • typenullの場合はfalse
  • typerefToAnotherの値に応じて次の値を返す
    • refToAnotherfalseならtrueを返す
    • refToAnothertrueの場合
      • refTypenullならfalseを返す
      • refTypeが空文字ならfalseを返す
      • refTypeTypetypeNameのいずれかと一致するならfalseを返す
      • それ以外ならtrueを返す

if 〜 else if 〜を使う

public static boolean wellDefined(PropertyEntry entry) {
    if (entry == null) {
        return false;
    }else if(entry.getName() == null) {
        return false;
    } else if (entry.getName().isEmpty() == false) {
        return false;
    } else if // ... 面倒になってきた
}

もう、なんか、3つ目のelse if描き始めた時に面倒さが噴出してきました。

Optionalを使う

まず、Predicate<PropertyEntry>を大量に用意します。

このときに用意するPredicate<PropertyEntry>はOKにしたいパターンの条件を書いていきます。

    // nameがnullでないときはOK
    private static final Predicate<PropertyEntry> NAME_NOT_NULL = e -> e.getName() != null;
    // nameが空文字でないときはOK
    private static final Predicate<PropertyEntry> NAME_NOT_EMPTY = e -> !e.getName().isEmpty();
    // 省略
    // refToAnotherしてないならOK
    private static final Predicate<PropertyEntry> TYPE_NOT_REF_TO_ANOTHER = e -> !e.getType().isRefToAnother();
    // refTypeがnullでないならOK
    private static final Predicate<PropertyEntry> REF_NOT_NULL = e -> e.getRefType() != null;
    // refTypeが空文字列でないならOK
    private static final Predicate<PropertyEntry> REF_NOT_EMPTY = e -> !e.getRefType().isEmpty();
    // refTypeがTypeのtypeName以外の名前ならOK
    private static final Predicate<PropertyEntry> REF_NOT_PROHIBITED = //省略
    // refTypeがちゃんと定義されている
    private static final Predicate<PropertyEntry> REF_WELL_DEFINED = REF_NOT_NULL
            .and(REF_NOT_EMPTY)
            .and(REF_NOT_PROHIBITED);

あとは、Optionalを使ってわかりやすい条件を書いていきます。

public static boolean wellDefined(PropertyEntry entry) {
    return Optional.ofNullable(entry)
            .filter(NAME_NOT_NULL)
            .filter(NAME_NOT_EMPTY)
            .filter(NAME_MATCHES_RULE)
            .filter(TYPE_NOT_NULL)
            .filter(TYPE_NOT_REF_TO_ANOTHER.or(REF_WELL_DEFINED))
            .map(e -> true)
            .orElse(false);
}

Predicateの名前を適切につけてやれば、if 〜 else if 〜より読みやすい!と思いませんか?

…思わない。はい、すみません(´・ω・`)


結論

もう、すでにやってる?すみません、仕事してないので最新のJava事情詳しくないんです。

www.amazon.co.jp

www.amazon.co.jp