Pintasan operator "atau penugasan" (| =) di Java

89

Saya memiliki serangkaian perbandingan panjang yang harus dilakukan di Java, dan saya ingin tahu apakah satu atau lebih dari mereka terbukti benar. String perbandingannya panjang dan sulit dibaca, jadi saya memutuskannya agar mudah dibaca, dan secara otomatis menggunakan operator pintasan |=daripada negativeValue = negativeValue || boolean.

boolean negativeValue = false;
negativeValue |= (defaultStock < 0);
negativeValue |= (defaultWholesale < 0);
negativeValue |= (defaultRetail < 0);
negativeValue |= (defaultDelivery < 0);

Saya berharap negativeValuemenjadi true jika salah satu nilai default <something> adalah negatif. Apakah ini sah? Akankah itu melakukan apa yang saya harapkan? Saya tidak dapat melihatnya disebutkan di situs Sun atau stackoverflow, tetapi Eclipse tampaknya tidak memiliki masalah dengannya dan kode tersebut terkompilasi dan berjalan.


Demikian pula, jika saya ingin melakukan beberapa persimpangan logis, dapatkah saya menggunakan &=sebagai gantinya &&?

David Mason
sumber
13
Mengapa Anda tidak mencobanya?
Roman
Ini adalah logika boolean umum, bukan hanya Java. sehingga Anda dapat mencarinya di tempat lain. Dan mengapa Anda tidak mencobanya saja?
Dykam
16
@Dykam: Tidak, ada perilaku khusus di sini. Java dapat memilih untuk membuat | = hubungan arus pendek, sehingga tidak akan mengevaluasi RHS jika LHS sudah benar - tetapi sebenarnya tidak.
Jon Skeet
2
@ Jon Skeet: Hubungan arus pendek akan sesuai untuk ||=operator yang tidak ada , tetapi |=merupakan bentuk kombinasi dari bitwise atau operator.
David Thornley
1
@ Jon Skeet: Tentu, tetapi membuat |=hubungan arus pendek tidak akan konsisten dengan operator penugasan gabungan lainnya, karena a |= b;tidak akan sama dengan a = a | b;, dengan peringatan biasa tentang mengevaluasi adua kali (jika itu penting). Bagi saya sepertinya keputusan perilaku bahasa besar tidak terjadi ||=, jadi saya kehilangan maksud Anda.
David Thornley

Jawaban:

204

Ini |=adalah operator penugasan gabungan ( JLS 15.26.2 ) untuk operator logika boolean |( JLS 15.22.2 ); jangan disamakan dengan kondisional-atau ||( JLS 15.24 ). Ada juga &=dan ^=sesuai dengan versi penugasan gabungan dari logika boolean &dan ^masing - masing.

Dengan kata lain, karena boolean b1, b2, keduanya setara:

 b1 |= b2;
 b1 = b1 | b2;

Perbedaan antara operator logika ( &dan |) dibandingkan dengan operator bersyaratnya ( &&dan ||) adalah bahwa operator logika sebelumnya tidak melakukan "shortcircuit"; yang terakhir lakukan. Itu adalah:

  • &dan | selalu evaluasi kedua operan
  • &&dan ||mengevaluasi operan kanan secara kondisional ; operan kanan dievaluasi hanya jika nilainya dapat mempengaruhi hasil operasi biner. Itu berarti operan yang tepat TIDAK dievaluasi ketika:
    • Operan kiri &&mengevaluasi kefalse
      • (karena tidak peduli apa operan yang tepat mengevaluasi, seluruh ekspresi adalah false)
    • Operan kiri ||mengevaluasi ketrue
      • (karena tidak peduli apa operan yang tepat mengevaluasi, seluruh ekspresi adalah true)

Jadi kembali ke pertanyaan awal Anda, ya, konstruksi itu valid, dan while |=bukan merupakan jalan pintas yang setara untuk =dan ||, itu menghitung apa yang Anda inginkan. Karena sisi kanan |=operator dalam penggunaan Anda adalah operasi perbandingan bilangan bulat sederhana, fakta bahwa |tidak ada hubungan pendek tidak signifikan.

Ada beberapa kasus, ketika sirkuit pendek diinginkan, atau bahkan diperlukan, tetapi skenario Anda bukan salah satunya.

Sayangnya, tidak seperti beberapa bahasa lain, Java tidak memiliki &&=dan ||=. Hal ini dibahas dalam pertanyaan Mengapa Java tidak memiliki versi penugasan gabungan dari operator bersyarat dan dan bersyarat atau? (&& =, || =) .

poligenelubricants
sumber
+1, sangat teliti. tampaknya masuk akal bahwa kompilator mungkin akan beralih ke operator hubung singkat, jika dapat menentukan bahwa RHS tidak memiliki efek samping. ada petunjuk tentang itu?
Carl
Saya membaca bahwa ketika RHS itu sepele dan SC tidak diperlukan, operator SC "pintar" sebenarnya sedikit lebih lambat. Jika benar, maka lebih menarik untuk bertanya-tanya apakah beberapa kompiler dapat mengubah SC ke NSC dalam keadaan tertentu.
poligenelubricants
Operator hubung singkat @polygenelubricants melibatkan semacam cabang di bawah kap, jadi jika tidak ada pola yang ramah prediksi-cabang ke nilai kebenaran yang digunakan dengan operator dan / atau arsitektur yang digunakan tidak memiliki prediksi cabang yang baik / apa pun ( dan dengan asumsi compiler dan / atau mesin virtual tidak melakukan optimasi terkait sendiri), maka ya, operator SC akan mengalami kelambatan dibandingkan dengan non-short-circuiting. Cara terbaik untuk mengetahui kompilator melakukan sesuatu adalah dengan mengkompilasi program menggunakan SC vs. NSC dan kemudian membandingkan bytecode untuk melihat apakah itu berbeda.
JAB
Juga, jangan bingung dengan operator OR bitwise yang juga |
user253751
16

Ini bukan operator "jalan pintas" (atau hubungan arus pendek) seperti itu || dan && are (karena mereka tidak akan mengevaluasi RHS jika mereka sudah mengetahui hasil berdasarkan LHS) tetapi akan melakukan apa yang Anda inginkan dalam hal bekerja .

Sebagai contoh perbedaannya, kode ini akan baik-baik saja jika textnull:

boolean nullOrEmpty = text == null || text.equals("")

sedangkan ini tidak akan:

boolean nullOrEmpty = false;
nullOrEmpty |= text == null;
nullOrEmpty |= text.equals(""); // Throws exception if text is null

(Jelas Anda dapat melakukannya "".equals(text)untuk kasus khusus itu - saya hanya mencoba mendemonstrasikan prinsipnya.)

Jon Skeet
sumber
3

Anda hanya bisa memiliki satu pernyataan. Dinyatakan dalam beberapa baris, pembacaannya hampir persis seperti kode sampel Anda, hanya saja yang kurang penting:

boolean negativeValue
    = defaultStock < 0 
    | defaultWholesale < 0
    | defaultRetail < 0
    | defaultDelivery < 0;

Untuk ekspresi yang paling sederhana, menggunakan |bisa lebih cepat daripada ||karena meskipun menghindari melakukan perbandingan, itu berarti menggunakan cabang secara implisit dan itu bisa berkali-kali lebih mahal.

Peter Lawrey
sumber
Saya memang mulai dengan hanya satu, tetapi seperti yang dinyatakan dalam pertanyaan awal saya merasa bahwa "Rangkaian perbandingannya panjang dan sulit dibaca, jadi saya memutuskannya agar mudah dibaca". Selain itu, dalam hal ini saya lebih tertarik mempelajari perilaku | = daripada membuat kode khusus ini berfungsi.
David Mason
1

Meskipun mungkin berlebihan untuk masalah Anda, pustaka Guava memiliki sintaks yang bagus dengan Predicates dan melakukan evaluasi sirkuit pendek atau / dan Predicates.

Pada dasarnya, perbandingan diubah menjadi objek, dikemas menjadi koleksi, dan kemudian diulangi. Untuk atau predikat, klik benar pertama akan kembali dari iterasi, dan sebaliknya untuk dan.

Carl
sumber
1

Jika ini tentang keterbacaan, saya punya konsep pemisahan data yang diuji dari logika pengujian. Contoh kode:

// declare data
DataType [] dataToTest = new DataType[] {
    defaultStock,
    defaultWholesale,
    defaultRetail,
    defaultDelivery
}

// define logic
boolean checkIfAnyNegative(DataType [] data) {
    boolean negativeValue = false;
    int i = 0;
    while (!negativeValue && i < data.length) {
        negativeValue = data[i++] < 0;
    }
    return negativeValue;
}

Kode terlihat lebih bertele-tele dan cukup jelas. Anda bahkan dapat membuat array dalam pemanggilan metode, seperti ini:

checkIfAnyNegative(new DataType[] {
    defaultStock,
    defaultWholesale,
    defaultRetail,
    defaultDelivery
});

Ini lebih mudah dibaca daripada 'string perbandingan', dan juga memiliki keunggulan kinerja dari hubungan arus pendek (dengan biaya alokasi array dan pemanggilan metode).

Sunting: Bahkan lebih banyak keterbacaan dapat dengan mudah dicapai dengan menggunakan parameter varargs:

Tanda tangan metode adalah:

boolean checkIfAnyNegative(DataType ... data)

Dan panggilannya bisa terlihat seperti ini:

checkIfAnyNegative( defaultStock, defaultWholesale, defaultRetail, defaultDelivery );
Krzysztof Jabłoński
sumber
1
Alokasi array dan pemanggilan metode adalah biaya yang cukup besar untuk korsleting, kecuali jika Anda memiliki operasi yang mahal dalam perbandingan (meskipun contoh dalam pertanyaan tersebut murah). Karena itu, sebagian besar waktu pemeliharaan kode akan mengalahkan pertimbangan kinerja. Saya mungkin akan menggunakan sesuatu seperti ini jika saya melakukan perbandingan secara berbeda di banyak tempat yang berbeda atau membandingkan lebih dari 4 nilai, tetapi untuk satu kasus ini agak bertele-tele untuk selera saya.
David Mason
@DvidaSaya setuju. Namun perlu diingat, bahwa sebagian besar kalkulator modern akan menelan biaya overhead semacam itu dalam waktu kurang dari beberapa milidetik. Secara pribadi saya tidak akan peduli tentang biaya overhead sampai masalah kinerja, yang tampaknya masuk akal . Selain itu, verbositas kode merupakan keuntungan, terutama jika javadoc tidak disediakan atau dibuat oleh JAutodoc.
Krzysztof Jabłoński
1

Ini adalah posting lama tetapi untuk memberikan perspektif yang berbeda untuk pemula, saya ingin memberikan contoh.

Saya pikir kasus penggunaan paling umum untuk operator gabungan serupa adalah +=. Saya yakin kita semua menulis sesuatu seperti ini:

int a = 10;   // a = 10
a += 5;   // a = 15

Apa gunanya ini? Intinya adalah untuk menghindari boilerplate dan menghilangkan kode berulang.

Jadi, baris berikutnya melakukan hal yang persis sama, menghindari mengetik variabel b1dua kali di baris yang sama.

b1 |= b2;
oxyt
sumber
1
Lebih sulit untuk menemukan kesalahan dalam operasi dan nama operator, terutama bila namanya panjang. longNameOfAccumulatorAVariable += 5; vs. longNameOfAccumulatorAVariable = longNameOfAccumulatorVVariable + 5;
Andrei
0
List<Integer> params = Arrays.asList (defaultStock, defaultWholesale, 
                                       defaultRetail, defaultDelivery);
int minParam = Collections.min (params);
negativeValue = minParam < 0;
Roma
sumber
Saya pikir saya lebih suka negativeValue = defaultStock < 0 || defaultWholesale < 0dll. Selain ketidakefisienan dari semua tinju dan pembungkus yang terjadi di sini, saya tidak menemukan itu hampir semudah memahami apa arti kode Anda sebenarnya.
Jon Skeet
Jika dia memiliki lebih dari 4 parameter dan kriterianya sama untuk semuanya maka saya suka solusi saya, tetapi demi keterbacaan, saya akan memisahkannya menjadi beberapa baris (yaitu membuat Daftar, temukan nilai min, bandingkan nilai minimum dengan 0) .
Roman
Itu memang terlihat berguna untuk banyak perbandingan. Dalam hal ini saya pikir pengurangan kejelasan tidak sebanding dengan penghematan dalam pengetikan dll.
David Mason
0

|| boolean logis ATAU
| bitwise ATAU

| = bitwise inklusif OR dan operator penugasan

Alasan mengapa | = tidak melakukan shortcircit adalah karena ia melakukan bitwise ATAU bukan OR logis. Artinya:

C | = 2 sama dengan C = C | 2

Tutorial untuk operator java

oneklc
sumber
TIDAK ada bitwise ATAU dengan boolean! Operatornya |integer bitwise OR tetapi juga logis OR - lihat Spesifikasi Bahasa Java 15.22.2.
pengguna85421
Anda benar karena untuk satu bit (a boolean) bitwise dan logis ATAU setara. Padahal secara praktis, hasilnya sama.
oneklc