web-dev-qa-db-ja.com

Javaに条件付きAND演算子と条件付きOR演算子の複合代入バージョンがないのはなぜですか?(&& =、|| =)

したがって、ブール値の二項演算子の場合、Javaには_&_、_|_、_^_、_&&_および_||_があります。

ここで彼らが何をするかを簡単にまとめましょう:

_&_の場合、両方のオペランドの値がtrueであれば、結果の値はtrueです。それ以外の場合、結果はfalseです。

_|_の場合、両方のオペランドの値がfalseであれば、結果の値はfalseです。それ以外の場合、結果はtrueです。

_^_の場合、オペランドの値が異なる場合、結果の値はtrueです。それ以外の場合、結果はfalseです。

_&&_演算子は_&_に似ていますが、左側のオペランドの値がtrueの場合にのみ、右側のオペランドを評価します。

_||_演算子は_|_と似ていますが、左側のオペランドの値がfalseの場合にのみ、右側のオペランドを評価します。

現在、5つすべてのうち、3つに_|=_、_&=_、および_^=_という複合代入バージョンがあります。だから私の質問は明らかです:Javaが_&&=_と_||=_も提供しないのはなぜですか?私は必要以上にそれらを必要としています_&=_および_|=_。

Javaには_>>>=_があるので、「長すぎるから」は良い答えだとは思いません。この省略にはもっと良い理由があるはずです。


から 15.26割り当て演算子

12個の代入演算子があります。 [...] _= *= /= %= += -= <<= >>= >>>= &= ^= |=_


_&&=_および_||=_が実装されている場合、最初に右側を評価しない唯一の演算子になるというコメントが行われました。複合代入演算子が最初に右辺を評価するというこの考え方は誤りだと思います。

から 15.26.2複合代入演算子

_E1 op= E2_形式の複合代入式はE1 = (T)((E1) op (E2))と同等です。ここで、Tは_E1_のタイプですが、_E1_のみが評価されます一度。

証明として、次のスニペットはNullPointerExceptionではなくArrayIndexOutOfBoundsExceptionをスローします。

_    int[] a = null;
    int[] b = {};
    a[0] += b[-1];
_
80

理由

演算子&&=および||=Javaでは使用できません。ほとんどの開発者にとって、これらの演算子は次のとおりです。

  • エラーを起こしやすい
  • 役に立たない

&&=の例

Java allowed &&=演算子の場合、そのコードは次のとおりです。

bool isOk = true; //becomes false when at least a function returns false
isOK &&= f1();
isOK &&= f2(); //we may expect f2() is called whatever the f1() returned value

次と同等になります:

bool isOk = true;
if (isOK) isOk = f1();
if (isOK) isOk = f2(); //f2() is called only when f1() returns true

この最初のコードはerror-proneです。これは、多くの開発者がf2()はf1()の戻り値が何であれ常に呼び出されると考えるためです。 bool isOk = f1() && f2();trueを返す場合にのみf2()が呼び出されるf1()のようなものです。

開発者がf2()trueを返すときにのみf1()を呼び出すことを望む場合、上記の2番目のコードはエラーが発生しにくくなります。

それ以外の場合、開発者はf2()を常に呼び出す必要があるため、&=で十分です。

同じ例ですが、&=の場合

bool isOk = true;
isOK &= f1();
isOK &= f2(); //f2() always called whatever the f1() returned value

さらに、JVMはこのコードを次のように実行する必要があります。

bool isOk = true;
if (!f1())  isOk = false;
if (!f2())  isOk = false;  //f2() always called

&&&の結果を比較する

ブール値に適用した場合、演算子&&&の結果は同じですか?

次のJavaコードを使用して確認してみましょう。

public class qalcdo {

    public static void main (String[] args) {
        test (true,  true);
        test (true,  false);
        test (false, false);
        test (false, true);
    }

    private static void test (boolean a, boolean b) {
        System.out.println (counter++ +  ") a=" + a + " and b=" + b);
        System.out.println ("a && b = " + (a && b));
        System.out.println ("a & b = "  + (a & b));
        System.out.println ("======================");
    }

    private static int counter = 1;
}

出力:

1) a=true and b=true
a && b = true
a & b = true
======================
2) a=true and b=false
a && b = false
a & b = false
======================
3) a=false and b=false
a && b = false
a & b = false
======================
4) a=false and b=true
a && b = false
a & b = false
======================

したがって[〜#〜] yes [〜#〜]ブール値の場合、&&&に置き換えることができます;-)

したがって、&=ではなく&&=を使用することをお勧めします。

||=も同様

&&=と同じ理由:
演算子|=||=よりもエラーが発生しにくくなっています。

開発者がf2()trueを返すときにf1()を呼び出さないようにしたい場合は、次の代替案をお勧めします。

// here a comment is required to explain that 
// f2() is not called when f1() returns false, and so on...
bool isOk = f1() || f2() || f3() || f4();

または:

// here the following comments are not required 
// (the code is enough understandable)
bool isOk = false;
if (!isOK) isOk = f1();
if (!isOK) isOk = f2(); //f2() is not called when f1() returns false
if (!isOK) isOk = f3(); //f3() is not called when f1() or f2() return false
if (!isOK) isOk = f4(); //f4() is not called when ...
25
olibre

おそらく何かのように

_x = false;
x &&= someComplexExpression();
_

looksxに割り当ててsomeComplexExpression()を評価する必要があるようですが、評価がxの値に依存しているという事実は構文から明らかです。

また、Javaの構文はCに基づいており、これらの演算子を追加する差し迫った必要性を見たことがないためです。とにかく、おそらくifステートメントの方が良いでしょう。

16
Josh Lee

Javaではこのようになっています。これはCではこのようになるためです。

なぜCでそうなのかという問題は、&と&&が異なる演算子になったとき(CがBから降下する前に時々)、&=の多様な演算子が単に見落とされたためです。

しかし、私の回答の2番目の部分には、それをバックアップするためのソースがありません。

9
EFraim

主な理由はJava構文はC(または少なくともCファミリー)に基づいており、Cではこれらの代入演算子はすべて、単一のレジスターで算術またはビットごとのアセンブリー命令にコンパイルされます。バージョンは一時を回避し、初期の非最適化コンパイラでより効率的なコードを生成した可能性があります。論理演算子(Cで呼ばれている)と同等の(&&=および||=)単一のアセンブリ命令との明確な対応はありません。それらは通常、命令のテストおよび分岐シーケンスに拡張されます。

興味深いことに、Ruby doのような言語には|| =と&& =があります。

編集:JavaとCの間で用語が異なります

4
p00ya

Javaの最初の aims の1つは、「シンプルで、オブジェクト指向で、使い慣れた」ものでした。このケースに適用される場合、&=はよく知られています(C、C++にはそれがあり、このコンテキストでよく知られているということは、これら2つを知っている人にとってはよく知られています)。

&& =は馴染みがなく、単純でもありません。つまり、言語設計者は、言語に追加できるすべての演算子を考えようとしていなかったので、余分な演算子が少ないほど単純になります。

4
Yishai

ブール変数の場合、&&および|| &と|の間に短絡評価を使用しますそうしないと、&& =と|| =が短絡評価も使用することになります。これには良いユースケースがあります。特にループを繰り返し処理する場合は、高速で効率的で簡潔である必要があります。

書く代わりに

foreach(item in coll)
{
   bVal = bVal || fn(item); // not so elegant
}

書きたい

foreach(item in coll)
{
  bVal ||= fn(item);    // elegant
}

また、bValがtrueになると、残りの反復でfn()が呼び出されなくなります。

3
zanfilip

'&'および '&&'は '&&'と同じではありません。'& 'の最初のオペランドがfalseの場合、この操作は行われません。とにかくそれを行います(数値とブール値の両方で機能します)。

存在する方が理にかなっていることに同意しますが、存在しなくてもそれほど悪くはありません。 Cにはないので、そこにはなかったと思います。

本当に理由は考えられません。

2
NawaMan

Rubyでは許可されています。

推測してみると、あまり使われていないので実装していません。別の説明は、パーサーが=

0
zzawaideh

a&bとa && bは同じものではありません。

a && bはブール値を返すブール式であり、a&bはintを返すビット単位の式です(aとbがintの場合)。

彼らは同じだと思いますか?

0
Omry Yadan

「それは信じられないほど醜く見えます!」

0

両方のオペランドを検証します。これはビット演算子です。 Javaは、long型、int型、short型、char型、およびbyte型の整数型に適用できるいくつかのビット単位演算子を定義します。

&&

結果がfalseになるため、最初のオペランドがfalseと評価されると評価を停止します。これは論理演算子です。ブール値に適用できます。

&&演算子は&演算子に似ていますが、コードを少し効率的にすることができます。式全体がtrueになるには、&演算子で比較される両方の式がtrueである必要があるため、最初の式がfalseを返した場合に2番目の式を評価する理由はありません。 &演算子は常に両方の式を評価します。 &&演算子は、最初の式が真の場合にのみ、2番目の式を評価します。

&& =代入演算子があっても、言語に新しい機能が追加されるわけではありません。ビット演算子の演算ははるかに表現力豊かで、ブール演算を含む整数ビット演算を実行できます。論理演算子はブール演算のみを実行できます。

0
Ely