ショートサーキット(短絡評価)とは
ショートサーキット(短絡評価)って、聞いたことはありますか?
「≪左辺≫ ≪論理演算子≫ ≪右辺≫」というような、論理演算子による式(論理演算子式)があるとする。左辺(第一引数)を評価した段階で式全体の値が定まらない場合のみ右辺(第二引数)を評価する、というのが短絡評価である。
短絡評価 - Wikipedia
Javaのショートサーキット演算子は、||
と&&
が該当します。
論理演算を行う際に使用する、「かつ」「または」を意味する論理演算子です。
どういうことか、具体的に説明していきましょう。
サンプルコード
isEmptyメソッドでは、String型引数をチェックし、そのチェック結果としてbooleanの値を返却します。
チェックする条件は「nullまたは空文字のいずれかである」ですが、「または」を意味する論理演算子がショートサーキット演算子の||
です。
private static boolean isEmpty(String str) {
if (Objects.isNull(str) || str.isEmpty()) {
return true;
}
return false;
}
下記のisEmptyメソッドに渡される引数strがnullの場合、左辺str == null
の評価結果はtrue
となります。
左辺の評価結果がtrue
ですので、右辺str.isEmpty()
の評価結果がtrue
、false
のいずれにおいてもif文の評価結果が変わることはありません。
よって右辺の評価は不要であり、評価自体を実行しません。評価処理のスキップです。
引数strが例えばnull以外であれば、右辺の評価が必要となり、str.isEmpty()
が実行されます。
左辺の評価結果で両辺の評価結果が確定する場合、不要な右辺の評価を実行しないのが**ショートサーキット(短絡評価)**です。
解説
||
演算子の場合、左辺または右辺のどちらかが真であるかどうかを評価します。
左辺が真である場合、右辺によって評価結果が真以外になることはないため、右辺を評価しません。
左辺が偽の場合は、右辺が真であれば評価結果は真ですし、偽であれば評価結果は偽になるため、右辺も評価をする必要があります。
&&
演算子の場合、左辺かつ右辺のいずれも真であるかを評価します。
左辺が偽である場合、右辺によって評価結果が偽以外になることはないため、右辺を評価しません。
左辺が真である場合は、右辺が真であれば評価結果は真ですし、偽であれば評価結果は偽になるため、右辺も評価をする必要があります。
論理演算子は、右辺と左辺のいずれもの評価を合わせて評価結果を算出します。
ですがショートサーキット演算子を用いることによって、片方の評価だけで済む、つまりは実行時の処理を簡略化することが可能です。
ショートサーキットの落とし穴
サンプルコードのisEmptyメソッドに渡される引数strがnullの場合、右辺str.isEmpty()
は実行されません。
つまり、if文の評価中にNullPointerException
は発生しません。
ですが、左辺と右辺を入れ替えたらどうでしょうか。
private static boolean isEmpty(String str) {
if (str.isEmpty() || Objects.isNull(str)) {
return true;
}
return false;
}
左辺str.isEmpty()
の評価を行った時点で、NullPointerException
が発生します。
このように左辺と右辺の評価順に対して、コーディング時に考慮が必要になる場合もあります。
論理演算子||
と&&
がショートサーキットである意識がない場合、左辺の評価によって右辺の評価自体が行われないことも意識できません。
Javaの論理演算子||
と&&
はショートサーキットであることを意識してのコーディングが必要です。
※補足 完全評価演算子について
Javaでは基本的に論理演算子として||
と&&
を使用します。
ですが、ショートサーキットではない、左辺と右辺を必ず評価する論理演算子も存在します。|
と&
です。
これを完全評価演算子といいます。完全ブール評価、完全論理評価ともいうそうです。
結構な年数Javaを書いてますが、使ったことはないです。常にショートサーキット前提ですね。
Javaは、ショートサーキット演算子が存在する言語です。
ですが、逆にショートサーキット演算子が存在しない言語もあります。
VBAなんかはそうですね。常に両辺を評価します。
以下はショートサーキット演算子のないVBAでのisEmptyメソッドの実装例です。
Javaのstr.isEmpty()
にあたる処理では、例外の発生を防ぐためにネストを深くする必要があります。
このようにショートサーキット演算子の存在しない言語では、下記の例のようにアーリーリターンで返すことや、メソッドを外出しするように気を付けないと、どんどんネストが深くなって可読性が落ちますね…
ちなみにVBAでは文字列String型にnullがないので(Len(vbNullString)
の結果は0
、vbNullString = ""
の結果はTrue
、Null
とEmpty
はVariant型専用)、Collection型で記載します。
Private Function IsEmpty(ByVal list As Collection) As Boolean
IsEmpty = True
If list Is Nothing Then
' null(的な状態)の場合
Exit Function
End If
If list.Count = 0 Then
' 要素数が0の場合
Exit Function
End If
IsEmpty = False
End Function
JavaのようにIf list Is Nothing Or list.Count = 0 Then
と記述してしまうと、引数list
がNothing
の場合に、JavaでいうNullPointerException
に該当する実行時エラー91
が発生します。
左辺の結果はtrue
でありその時点でIf文全体の評価は確定しますが、右辺も評価処理を行うためです。
また、If list.Count = 0 Then
のIf文を先に記述した場合も実行時エラーが発生します。
複数条件を組み合わせた判定を行う場合、ショートサーキット演算子の有無にかかわらず条件判定の記述順は、考慮が必要です。