Dlaczego w Javie nie ma złożonych wersji przypisań operatorów warunkowych i warunkowych lub? (&& =, || =)


83

Więc dla operatorów binarnych logicznych, Java ma &, |, ^, &&i ||.

Podsumujmy pokrótce, co robią tutaj:

Dla &wartości truewynikowej jest, jeśli obie wartości operandów są true; w przeciwnym razie wynik to false.

Dla |wartości falsewynikowej jest, jeśli obie wartości operandów są false; w przeciwnym razie wynik to true.

Dla ^wartości truewynikowej jest to, że wartości argumentów są różne; w przeciwnym razie wynik to false.

&&Operator jest jak &ale ocenia jego prawy operand tylko wtedy, gdy wartość jego lewostronny operand jest true.

||Operator jest podobny |, ale ocenia jego prawy operand tylko wtedy, gdy wartość jego lewostronny operand jest false.

Teraz spośród wszystkich 5, 3 z nich mają złożone wersje zadania, a mianowicie |=, &=i ^=. Więc moje pytanie jest oczywiste: dlaczego Java nie zapewnia &&=i ||=również? Uważam, że potrzebuję ich bardziej niż potrzebuję &=i |=.

I nie uważam, że „ponieważ jest za długi” to dobra odpowiedź, ponieważ Java tak >>>=. Musi być lepszy powód tego zaniedbania.


Od 15.26 Operatory przypisania :

Istnieje 12 operatorów przypisania; […]= *= /= %= += -= <<= >>= >>>= &= ^= |=


Pojawiła się uwaga, że ​​gdyby &&=i ||=zostały zaimplementowane, byliby jedynymi operatorami, którzy nie oceniliby najpierw prawej strony. Uważam, że błędem jest założenie, że operator przypisania złożonego najpierw ocenia prawą stronę.

Od 15.26.2 Operatory przypisania złożonego :

Wyrażenie przypisania złożonego formularza E1 op= E2jest równoważne z E1 = (T)((E1) op (E2)), gdzie Tjest typem E1, z tą różnicą, że E1jest oceniane tylko raz.

Jako dowód poniższy fragment kodu zgłasza a NullPointerException, a nie ArrayIndexOutOfBoundsException.

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

2
Idę na drugie, nikogo to nie obchodzi: P również, wszystkie te pytania „dlaczego funkcja x nie jest w języku y?” należy zapytać projektantów języka, a nie nas: P
Federico klez Culloca

1
Co oznacza & =? Ktoś może mi powiedzieć?
Tarik

@Aaron: a = a & b. Jest napisane w pytaniu
Federico klez Culloca


1
@jleedev: To pytanie jest starsze, ale ma więcej głosów i przychodzących linków. Powiedziałbym, że jeśli jest jakieś scalanie, połącz stary z tym (tak, można to zrobić).
poligenelubricants

Odpowiedzi:


28

Powód

Operatory &&=i ||=nie są dostępne w Javie, ponieważ dla większości programistów są to:

  • podatne na błędy
  • bezużyteczny

Przykład dla &&=

Jeśli Java dopuszcza &&=operator, to ten kod:

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

byłoby równoważne z:

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

Ten pierwszy kod jest podatny na błędy, ponieważ wielu programistów pomyślałoby, że f2()zawsze jest wywoływany niezależnie od wartości zwróconej przez f1 (). To jak bool isOk = f1() && f2();, gdzie f2()jest wywoływana tylko kiedy f1()powróci true.

Jeśli programista chce, f2()aby wywoływano go tylko wtedy , gdy f1()zwraca true, drugi kod powyżej jest mniej podatny na błędy.

W przeciwnym razie &=wystarczy, ponieważ programista chce, f2()aby zawsze nazywał się:

Ten sam przykład, ale dla &=

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

Ponadto JVM powinna uruchomić powyższy kod w następujący sposób:

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

Porównanie &&i &wyniki

Czy wyniki operatorów &&i są &takie same, gdy zostaną zastosowane do wartości logicznych?

Sprawdźmy, używając następującego kodu 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;
}

Wynik:

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
======================

Dlatego TAK możemy zastąpić &&przez &do wartości logicznych ;-)

Więc lepiej użyj &=zamiast &&=.

To samo dotyczy ||=

Te same powody co dla &&=:
operator |=jest mniej podatny na błędy niż ||=.

Jeśli programista f2()nie chce, aby dzwoniono po f1()powrocie true, radzę następujące alternatywy:

// 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();

lub:

// 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 ...

1
Cześć @StriplingWarrior. Sprawdziłem z moim kolegą Yannickiem, naszym najlepszym ekspertem od Java. Zaktualizowałem moją odpowiedź, używając źródła kodu Java używanego do sprawdzenia tego punktu. Jak powiedziałeś &i &&daj te same wyniki. Dziękujemy bardzo za Twoją opinię. Podoba ci się moja odpowiedź? Twoje zdrowie.
oHo

2
A jeśli chcę to zrobić bardzo szybko? && = byłby szybszy niż & =, gdyby istniał, więc powinieneś użyć go if (a) a = bdo przyspieszenia
adventurerOK,

Cześć @adventurerOK. Przepraszam, nie jestem pewien, czy rozumiem, co masz na myśli ... Myślę, że a&=b;jest szybszy niż if(a) a=b;przy użyciu wartości przechowywanych w rejestrach procesora. Jeśli jednak bznajduje się w pamięci zewnętrznej (nie jest buforowany), if(a) a=b;jest szybszy. Czy to masz na myśli? Proszę podać więcej przykładowego kodu ;-) Ciekawi mnie Twoja opinia. Do zobaczenia. Pozdrawiam
oHo,

13
Nie zgadzam się, kiedy mówisz: „A nie tego chcemy”. Jeśli piszę isOK &&= f2();, chciałbym, żeby tak samo jak to się &&zwierało.
Tor Klingberg

4
Nie zgadzam się z twoim twierdzeniem, że operatorzy byliby podatni na błędy lub bezużyteczni. Korzystanie z przypisań złożonych to coś, co robisz, aby iść na skróty do zwykłych A = A op B, aby każdy doskonale wiedział, co robią i może sam zająć się konsekwencjami. Gdyby twoje powody rzeczywiście były przyczyną jego braku, uznałbym to za niechciane protekcjonalne. Chciałbym jednak podziękować za dodanie tej kwestii, bool isOk = f1() || f2() || f3() || f4();ponieważ właśnie tak byłem oślepiony, aby zobaczyć siebie.
Franz B.

17

Pewnie dlatego, że coś takiego

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

wygląda na to, że powinien być przypisywany do xi oceniający someComplexExpression(), ale fakt, że ocena zależy od wartości, xnie wynika ze składni.

Również dlatego, że składnia Javy jest oparta na języku C i nikt nie widział pilnej potrzeby dodawania tych operatorów. I tak prawdopodobnie lepiej byłoby, gdybyś miał oświadczenie if.


38
Myślę, że to nie jest dobra odpowiedź, ponieważ można argumentować, że x() && y() wygląda na to, że należy oceniać obie strony wyrażenia. Oczywiście ludzie akceptują, że &&jest to zwarcie, więc powinno to również wynikać &&=.
polygenelubricants

2
@jleedev, zgodził się. Uważam, że w takich sytuacjach należy pamiętać, że nie jest to odpowiednik x = x && someComplexExpression (), ale odpowiednik x = someComplexExpression () && x. Prawa strona zostanie / powinna zostać oceniona jako pierwsza, aby była spójna z każdym innym operatorem przypisania. Biorąc to pod uwagę, && = nie zachowałoby się inaczej niż & =.
PSpeed

@polygene To fair. To, jak coś wygląda, zależy od tego, co znasz, i myślę, że nie chcieli dodawać niczego nowego. Jedną z rzeczy, które lubię w C / C ++ / Java / C #, jest to, że podstawowa składnia wyrażeń jest prawie identyczna.
Josh Lee

1
@PSpeed, mylisz się. JLS bardzo jasno określa, co ma robić przypisanie złożone. Zobacz mój dodatek powyżej.
polygenelubricants

1
@PSpeed: W takim przypadku a -= b;działałoby zupełnie inaczej niż a &&= b;.
David Thornley,

11

Tak jest w Javie, bo tak jest w C.

Teraz pytanie, dlaczego tak jest w C, jest takie, ponieważ kiedy & i && stały się różnymi operatorami (kiedyś poprzedzając pochodzenie C od B), po prostu przeoczono różnorodność operatorów & =.

Ale druga część mojej odpowiedzi nie ma żadnych źródeł, które mogłyby ją potwierdzić.


1
Co zabawne - pytanie zostało niedawno zadane na forum C; a ta odpowiedź została połączona (choć nie została oznaczona jako duplikat), co sprawia, że ​​uzupełnia argument cykliczny!
UKMonkey

6

Głównie dlatego, że składnia Javy jest oparta na języku C (lub przynajmniej na rodzinie C), aw języku C wszystkie te operatory przypisania są kompilowane do arytmetycznych lub bitowych instrukcji asemblera w jednym rejestrze. Wersja operatora przypisania unika tymczasowych i mogła wytworzyć bardziej wydajny kod na wczesnych nieoptymalizujących kompilatorach. Operator logiczny (jak określa się je w C) odpowiedniki (&&= i ||=) nie mają tak oczywistego odpowiednika z pojedynczymi instrukcjami asemblera; zwykle rozszerzają się na testową i rozgałęziającą sekwencję instrukcji.

Co ciekawe, języki takie jak rubin nie posiada || = i = &&.

Edycja: terminologia różni się w językach Java i C


Myślę, że masz na myśli, że odpowiedniki operatorów warunkowych nie mają tak oczywistej zgodności. W terminologii JLS logicznymi operatorami logicznymi są &:| i ^; &&i ||są operatorami warunkowymi.
polygenelubricants

W terminologii C, && i || są „operatorami logicznymi”, s6.5.13-14 w ISO 9899: 1999. Operatory bitowe są „logiczne” tylko wtedy, gdy są stosowane do pojedynczego bitu (wartość logiczna w Javie); w C nie ma typu jednobitowego, a operatory logiczne mają zastosowanie do wszystkich typów skalarnych.
p00ya

przed C99 C nie miał nawet booltypu. To 20 lat w historii języka bez bool. Nie było powodu &&=. wystarczyły operacje bitowe.
v.oddou

4

Jednym z pierwotnych celów Javy było bycie „prostym, zorientowanym obiektowo i znajomym”. W zastosowaniu do tego przypadku & = jest znajome (C, C ++ mają to i znane w tym kontekście oznacza znajomość dla kogoś, kto zna te dwa).

&& = nie byłoby znane i nie byłoby proste w tym sensie, że projektanci języka nie chcieli myśleć o każdym operatorze, który mogliby dodać do języka, więc mniej dodatkowych operatorów jest prostszych.


3

Dla zmiennych boolowskich, && i || użyłby oceny zwarcia while & i | nie rób tego, więc można by oczekiwać, że && = i || = będą również używać oceny zwarć. Jest to dobry przypadek użycia. Zwłaszcza jeśli iterujesz po pętli, chcesz działać szybko, wydajnie i zwięźle.

Zamiast pisać

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

Chcę pisać

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

i wiedz, że gdy bVal jest prawdziwe, fn () nie będzie wywoływane w pozostałej części iteracji.


1
W przypadku tej pętli prawdopodobnie po prostu chcesz to zrobić if (fn(item)) { bVal = true; break; }.
Radiodef

2

&” i „ &&” nie są tym samym, co „ &&” to operacja skrótu, która nie zadziała, jeśli pierwszy operand jest fałszywy, podczas gdy „& ” i tak to zrobi (działa zarówno z liczbą, jak i wartością logiczną).

Zgadzam się, że istnienie ma większy sens, ale nie jest tak źle, jeśli go nie ma. Chyba go tam nie było, ponieważ C go nie ma.

Naprawdę nie mogę myśleć, dlaczego.


0

Jest to dozwolone w Rubim.

Gdybym miał zgadywać, powiedziałbym, że nie jest często używany, więc nie został zaimplementowany. Innym wyjaśnieniem może być to, że parser patrzy tylko na znak przed znakiem =


1
Java obsługuje << =, >> = i >>> =, więc nie jest to do końca prawdą.
Yishai

prawdziwe. Nie myślałem o tym. Myślę, że jedynym wyjaśnieniem jest wtedy, jak często jest używany.
zzawaideh

0

Nie mogę wymyślić lepszego powodu niż „Wygląda niesamowicie brzydko!”


9
Ale C / Java nigdy nie miało być piękne.
EFraim

1
Ledwo uważam &&=się za brzydkiego
asgs

0

&

weryfikuje oba operandy, jest to operator bitowy. Java definiuje kilka operatorów bitowych, które można zastosować do typów całkowitych, long, int, short, char i byte.

&&

przestaje oceniać, jeśli pierwszy operand ma wartość false, ponieważ wynik będzie fałszywy, jest to operator logiczny. Można go zastosować do wartości logicznych.

Operator && jest podobny do operatora &, ale może sprawić, że kod będzie bardziej wydajny. Ponieważ oba wyrażenia porównywane przez operator & muszą być prawdziwe, aby całe wyrażenie było prawdziwe, nie ma powodu, aby oceniać drugie wyrażenie, jeśli pierwsze zwróci fałsz. Operator & zawsze oblicza oba wyrażenia. Operator && ocenia drugie wyrażenie tylko wtedy, gdy pierwsze wyrażenie jest prawdziwe.

Posiadanie operatora przypisania && = tak naprawdę nie dodałoby nowej funkcjonalności do języka. Arytmetyka operatora bitowego jest znacznie bardziej wyrazista, możesz wykonywać arytmetykę bitową na liczbach całkowitych, która obejmuje arytmetykę boolowską. Operatory logiczne mogą po prostu wykonywać arytmetykę boolowską.


0

Brian Goetz (architekt języka Java w Oracle) napisał :

https://stackoverflow.com/q/2324549/ [to pytanie] pokazuje, że istnieje zainteresowanie posiadaniem tych operatorów i nie ma jasnych argumentów, dlaczego jeszcze nie istnieją. Pytanie brzmi zatem: czy zespół JDK omawiał dodawanie tych operatorów w przeszłości, a jeśli tak, to jakie są powody, dla których nie należy ich dodawać?

Nie znam żadnej konkretnej dyskusji na ten temat, ale gdyby ktoś ją zaproponował, odpowiedź prawdopodobnie brzmiałaby: nie jest to nierozsądna prośba, ale nie ma jej wagi.

„Noszenie swojej wagi” należy oceniać na podstawie kosztów i korzyści oraz stosunku kosztów do korzyści w porównaniu z innymi kandydatami.

Myślę, że zakładasz w sposób dorozumiany (używając wyrażenia „istnieje odsetki”), że koszt jest bliski zeru, a korzyść jest większa od zera, więc wydaje się oczywistą wygraną. Ale to przeczy błędnemu zrozumieniu kosztów; taka funkcja wpływa na specyfikację języka, implementację, JCK i każdy podręcznik IDE i Java. Nie ma trywialnych funkcji językowych. Korzyści, chociaż niezerowe, są dość małe.

Po drugie, istnieje nieskończenie wiele funkcji, które moglibyśmy zrobić, ale co kilka lat możemy wykonać tylko kilka (a użytkownicy mają ograniczoną zdolność wchłaniania nowych funkcji). Dlatego musimy bardzo uważać, które wybieramy, ponieważ każda funkcja (nawet z pozoru banalna) pochłania część tego budżetu i nieodmiennie odbiera go innym.
To nie jest „dlaczego nie ta funkcja”, ale „jakich innych funkcji nie zrobimy (lub nie opóźnimy), abyśmy mogli to zrobić, i czy to dobra wymiana?” I naprawdę nie mogę sobie wyobrazić, żeby to była dobra transakcja w porównaniu z czymkolwiek innym, nad czym pracujemy.

Tak więc usuwa poprzeczkę z „niezłego pomysłu” (co jest już całkiem niezłe, wiele próśb o funkcje nawet tego nie wyjaśnia), ale wydaje się mało prawdopodobne, aby kiedykolwiek wyczyścił poprzeczkę „lepszego wykorzystania naszego budżetu ewolucji niż cokolwiek innego."


-1

a & b oraz a && b to nie to samo.

a && b jest wyrażeniem logicznym, które zwraca wartość logiczną, a a & b jest wyrażeniem bitowym, które zwraca liczbę int (jeśli a i b są liczbami int).

jak myślisz, dlaczego są takie same?


1
a & b powinno w przeciwieństwie do a && b zawsze oceniać b.
Daniel Brückner
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.