wake-up-neo.com

Shortcut-Operator "oder -Zuweisung" (| =) in Java

Ich habe eine lange Reihe von Vergleichen in Java, und ich würde gerne wissen, ob einer oder mehrere von ihnen als wahr gelten. Die Vergleichskette war lang und schwer zu lesen, daher habe ich sie aus Gründen der Lesbarkeit aufgeschlüsselt und automatisch einen Verknüpfungsoperator |= anstelle von negativeValue = negativeValue || boolean verwendet.

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

Ich gehe davon aus, dass negativeValue wahr ist, wenn einer der Standardwerte <etwas> negativ ist. Ist das gültig? Wird es das tun, was ich erwarte? Ich konnte es nicht auf Suns Site oder Stackoverflow erwähnen, aber Eclipse scheint kein Problem damit zu haben und der Code wird kompiliert und ausgeführt.


Wenn ich mehrere logische Kreuzungen durchführen wollte, könnte ich &= anstelle von && verwenden?

79
David Mason

|= ist ein zusammengesetzter Zuweisungsoperator ( JLS 15.26.2 ) für den booleschen logischen Operator | ( JLS 15.22.2 ); nicht mit dem bedingten -oder || ( JLS 15.24 ) verwechseln. Es gibt auch &= und ^=, die der zusammengesetzten Zuweisungsversion des booleschen logischen & und ^ entsprechen.

Mit anderen Worten, für boolean b1, b2 sind diese beiden gleichwertig:

 b1 |= b2;
 b1 = b1 | b2;

Der Unterschied zwischen den logischen Operatoren (& und |) im Vergleich zu ihren bedingten Gegenstücken (&& und ||) besteht darin, dass die ersteren keinen "Kurzschluss" machen; die letzteren tun. Das ist:

  • & und |always werten beide Operanden aus
  • && und || werten den rechten Operanden bedingt aus; Der rechte Operand wird nur ausgewertet, wenn sein Wert das Ergebnis der binären Operation beeinflussen kann. Das heißt, der rechte Operand wird NICHT ausgewertet, wenn:
    • Der linke Operand von && wird zu false.__ ausgewertet.
      • (denn egal, zu welchem ​​Wert der rechte Operand ausgewertet wird, der gesamte Ausdruck ist false)
    • Der linke Operand von || wird zu true.__ ausgewertet.
      • (denn egal, zu welchem ​​Wert der rechte Operand ausgewertet wird, der gesamte Ausdruck ist true)

Um zu Ihrer ursprünglichen Frage zurückzukehren, ja, das Konstrukt ist gültig, und obwohl |= nicht genau eine äquivalente Verknüpfung für = und || ist, berechnet es, was Sie wollen. Da die rechte Seite des |=-Operators in Ihrer Verwendung eine einfache Ganzzahlvergleichsoperation ist, ist die Tatsache, dass | keinen Kurzschluss darstellt, unbedeutend.

Es gibt Fälle, in denen ein Kurzschluss gewünscht oder sogar erforderlich ist, aber Ihr Szenario ist kein Fall.

Es ist bedauerlich, dass Java im Gegensatz zu einigen anderen Sprachen nicht &&= und ||= hat. Dies wurde in der Frage Warum enthält Java keine zusammengesetzten Zuweisungsversionen der Bedingungs- und Bedingungsoperatoren (&& =, || =) }.

188

Es ist kein Operator für "Abkürzungen" (oder Kurzschlüsse) in der Art und Weise, dass || und && sind (insofern sie die RHS nicht auswerten, wenn sie das Ergebnis bereits anhand der LHS kennen), aber es wird tun, was Sie wollen in Bezug auf Working .

Als Beispiel für den Unterschied ist dieser Code in Ordnung, wenn text null ist:

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

während dies nicht:

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

(Natürlich könnten Sie für diesen speziellen Fall "".equals(text) tun - ich versuche nur, das Prinzip zu demonstrieren.)

16
Jon Skeet

Sie könnten nur eine Aussage haben. Über mehrere Zeilen ausgedrückt, liest es fast genau wie Ihr Beispielcode, nur weniger wichtig:

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

Für die einfachsten Ausdrücke kann die Verwendung von | schneller als || sein, da ein Vergleich vermieden wird, obwohl ein Zweig implizit verwendet wird.

3
Peter Lawrey

Die Guava -Bibliothek enthält zwar eine Nizza-Syntax mit Predicates und führt eine Kurzschlussbewertung von oder/und Predicates aus.

Im Wesentlichen werden die Vergleiche in Objekte umgewandelt, in eine Sammlung gepackt und dann wiederholt. Für oder Prädikate wird der erste wahre Treffer von der Iteration zurückgegeben und umgekehrt für und.

1
Carl

Wenn es um Lesbarkeit geht, habe ich das Konzept der Trennung der getesteten Daten von der Testlogik erhalten. Codebeispiel:

// 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;
}

Der Code sieht ausführlicher und selbsterklärend aus. Sie können sogar ein Array im Methodenaufruf wie folgt erstellen:

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

Es ist besser lesbar als 'Vergleichsstring' und hat auch einen Leistungsvorteil beim Kurzschließen (auf Kosten der Array-Zuordnung und Methodenaufruf).

Edit: Noch mehr Lesbarkeit lässt sich einfach mit varargs-Parametern erreichen:

Methodensignatur wäre:

boolean checkIfAnyNegative(DataType ... data)

Und der Anruf könnte so aussehen:

checkIfAnyNegative( defaultStock, defaultWholesale, defaultRetail, defaultDelivery );

|| logisch boolean OR 
| bitweises ODER

| = bitweise inklusive OR und Zuweisungsoperator

Der Grund, warum | = nicht kurzgeschlossen wird, liegt darin, dass ein bitweises OR nicht ein logisches ODER ist. Das heißt:

 C | = 2 ist gleich C = C | 2 

Tutorial für Java-Operatoren

0
oneklc

Es ist ein alter Beitrag, aber um Anfängern eine andere Perspektive zu geben, möchte ich ein Beispiel geben. 

Ich denke, der häufigste Anwendungsfall für einen ähnlichen zusammengesetzten Operator wäre +=. Ich bin mir sicher, dass wir alle so etwas geschrieben haben:

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

Was war der Punkt davon? Die Variable a sollte nicht zweimal in dieselbe Zeile geschrieben werden. (Fehlt mir hier etwas überlegenes?)

Die nächste Zeile macht also genau das Gleiche und vermeidet, die Variable b1 zweimal in dieselbe Zeile einzugeben.

b1 |= b2;
0
oxyt
List<Integer> params = Arrays.asList (defaultStock, defaultWholesale, 
                                       defaultRetail, defaultDelivery);
int minParam = Collections.min (params);
negativeValue = minParam < 0;
0
Roman