🧮

【Java】 論理演算子を用いたらこんなにシンプルになるなんて! '^'も活用してみよう

2024/07/07に公開

この記事について

論理演算子はよく使いますよね?よく使われるものといえば、論理積(&&)、論理和(||)、論理否定(!)を駆使しながら条件文や、bool値の判定に活用しているかと思います。
ここに新しく排他的論理和(^)も押さえておくとシンプルにかけます。というか書けることを知りました。今回はその解説です。

先に結論を3つ

  • 「trueとfalse」の組み合わせになった場合にtrueを返すことを表す排他的論理和というものがある
  • 排他的論理和を^で表すことができる。
  • 論理積(&&)、論理和(||)、論理否定(!)を駆使しながら書くより圧倒的にシンプル

排他的論理和とは

論理演算の1つで、「trueとfalse」の組み合わせになった場合にtrueを返す演算のことです。XORと記載される場合もあります。
IPAの試験を受けられた方だとお馴染みカモしれません。
主にコンピュータが処理する最小単位であるbitを処理する際に活用されます。

以下のようなパターンです。

A B A XOR B
0 0 0
0 1 1
1 0 1
1 1 0

こんな処理を考えてみましょう

以下のようなデータを登録するとします

電話番号とメールアドレスのどちらかのみを登録させるという仕様だとします。
両方は登録させません。(そんな仕様はないこともないと思う。。。)
リクエストのクラスは以下のような想定です。

public class PostUserDataRequest {
    @NotNull
    private String name;

    @Nullable
    private String phoneNumber;

    @Nullable
    private String mailAddress;
}

名前は必須、電話番号とメールアドレスはnullでも良いとします。

今までの自分だったら以下のように書いています

電話番号とメールアドレスのどちらかのみを登録させるならばクラスを作成する段階でチェックすれば良いかなと思います。その際に以下のような処理でチェックします。

public boolean hasEitherPhoneNumberOrMailAddressNew() {
    if (Objects.isNull(this.phoneNumber) && Objects.isNull(this.mailAddress)) {
        return false;
    } else if (Objects.isNull(this.phoneNumber)) {
        return true;
    } else {
        return Objects.isNull(this.mailAddress);
    }
}

テストを書いてチェックします。

public class PostUserDataRequestTest {
    private static final String NAME = "パイロットユーザー";
    private static final String PHONE_NUMBER = "090-1234-5678";
    private static final String MAIL_ADDRESS = "A@example.com";

    @Nested
    @DisplayName("PhoneNumberとMailAddressのどちらか一方の値がnullであればtrueを返すかを判定するhasEitherPhoneNumberOrMailAddressのテスト")
    class HasEitherPhoneNumberOrMailAddressOld {
        @Test
        void PhoneNumberのみnullの場合trueを返すこと () {
            var request = new PostUserDataRequest(
                    NAME,
                    null,
                    MAIL_ADDRESS
            );

            Assertions.assertTrue(request.hasEitherPhoneNumberOrMailAddress());
        }

        @Test
        void MailAddressのみnullの場合trueを返すこと() {
            var request = new PostUserDataRequest(
                    NAME,
                    PHONE_NUMBER,
                    null
            );

            Assertions.assertTrue(request.hasEitherPhoneNumberOrMailAddress());
        }

        @Test
        void MailAddressPhoneNumberの両方の値がnullの場合falseを返すこと() {
            var request = new PostUserDataRequest(
                    NAME,
                    null,
                    null
            );

            Assertions.assertFalse(request.hasEitherPhoneNumberOrMailAddress());
        }

        @Test
        void MailAddressPhoneNumberの両方の値が存在する場合falseを返すこと() {
            var request = new PostUserDataRequest(
                    NAME,
                    PHONE_NUMBER,
                    MAIL_ADDRESS
            );

            Assertions.assertFalse(request.hasEitherPhoneNumberOrMailAddress());
        }
    }
}

実行します

> Task :compileJava
> Task :processResources UP-TO-DATE
> Task :classes
> Task :compileTestJava
> Task :processTestResources NO-SOURCE
> Task :testClasses
> Task :test
Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
For more on this, please refer to https://docs.gradle.org/8.8/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
BUILD SUCCESSFUL in 2s
4 actionable tasks: 3 executed, 1 up-to-date
20:31:25: タスク  ':test --tests "PostUserDataRequestTest"' の実行を完了しました。

これでも問題ないですが、、、

排他的論理和を使いましょう

排他的論理和を使うと以下のように書けます。

public boolean hasEitherPhoneNumberOrMailAddressOld() {
    return Objects.isNull(this.phoneNumber) ^ Objects.isNull(this.mailAddress);
}

一瞬です。先ほど書いたテストコードで再度チェックしても問題なく通ります。
こんな使い方があるとは。。。

最後に

排他的論理和は知ってはいたものの、活用する機会は来ないのかな〜と思っていました。しかし、プログラムを書く中で使うとは、取り込んだ知識がどこで生かされるかはわからないものですね。

これからも勉学を続けていこうと思います。

Discussion