Tại sao Java không có các phiên bản gán ghép của toán tử điều kiện và và điều kiện-hoặc? (&& =, || =)


82

Vì vậy, các nhà khai thác nhị phân trên dữ liệu boolean, Java có &, |, ^, &&||.

Hãy tóm tắt ngắn gọn những gì họ làm ở đây:

Đối với &, giá trị kết quả là truenếu cả hai giá trị toán hạng là true; nếu không, kết quả là false.

Đối với |, giá trị kết quả là falsenếu cả hai giá trị toán hạng là false; nếu không, kết quả là true.

Đối với ^, giá trị kết quả là truenếu các giá trị toán hạng khác nhau; nếu không, kết quả là false.

Các &&nhà điều hành cũng giống như &nhưng đánh giá bên tay phải của nó toán hạng chỉ nếu giá trị của trái tay của toán hạng là true.

Các ||nhà điều hành cũng giống như |, nhưng đánh giá bên tay phải của nó toán hạng chỉ nếu giá trị của trái tay của toán hạng là false.

Bây giờ, trong số tất cả 5, 3 trong số những người có các phiên bản chuyển nhượng hợp chất, cụ thể là |=, &=^=. Vì vậy, câu hỏi của tôi là hiển nhiên: tại sao Java không cung cấp &&=||=cũng như vậy? Tôi thấy rằng tôi cần những thứ đó nhiều hơn những gì tôi cần &=|=.

Và tôi không nghĩ rằng "bởi vì nó quá dài" là một câu trả lời tốt, bởi vì Java có >>>=. Phải có một lý do tốt hơn cho sự thiếu sót này.


Từ 15,26 Người điều hành nhiệm vụ :

Có 12 toán tử gán; [...]= *= /= %= += -= <<= >>= >>>= &= ^= |=


Một nhận xét đã được đưa ra rằng nếu &&=||=được thực hiện, thì đó sẽ là các toán tử duy nhất không đánh giá phía bên phải trước. Tôi tin rằng khái niệm rằng một toán tử gán ghép đánh giá phía bên phải trước tiên là một sai lầm.

Từ 15.26.2 Các nhà khai thác chuyển nhượng hợp chất :

Biểu thức gán ghép có dạng E1 op= E2tương đương với E1 = (T)((E1) op (E2)), đâu Tlà kiểu E1, ngoại trừ biểu thức chỉ E1được đánh giá một lần.

Để chứng minh, đoạn mã sau ném một NullPointerException, không phải một ArrayIndexOutOfBoundsException.

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

2
Tôi đi tiếp thứ hai, không ai quan tâm: P cũng vậy, tất cả những câu hỏi này về 'tại sao đặc trưng x không có trong ngôn ngữ y?' Nên yêu cầu nhà thiết kế của ngôn ngữ, chứ không phải cho chúng tôi: P
Federico Klez Culloca

1
& = Có nghĩa là gì? Ai đó vui lòng cho tôi biết?
Tarik

@Aaron: a = a & b. Nó được viết trong câu hỏi
Federico Klez Culloca


1
@jleedev: Câu hỏi đó cũ hơn, nhưng câu hỏi này có nhiều phiếu bầu và liên kết đến hơn. Tôi muốn nói nếu có bất kỳ hợp nhất nào, hãy hợp nhất cái cũ với cái này (vâng, điều đó có thể được thực hiện).
polygenelubricants,

Câu trả lời:


27

Lý do

Các toán tử &&=||=không có sẵn trên Java vì đối với hầu hết các nhà phát triển, các toán tử này là:

  • dễ bị lỗi
  • vô ích

Ví dụ như &&=

Nếu Java cho phép &&=toán tử, thì mã đó:

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

sẽ tương đương với:

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

Đoạn mã đầu tiên này dễ xảy ra lỗi vì nhiều nhà phát triển nghĩ rằng f2()luôn được gọi là bất kể giá trị trả về f1 () nào. Nó giống như bool isOk = f1() && f2();nơi f2()chỉ được gọi khi f1()trả về true.

Nếu nhà phát triển chỉ muốn f2()được gọi khi f1()trả về true, do đó mã thứ hai ở trên ít bị lỗi hơn.

Khác &=là đủ vì nhà phát triển muốn f2()luôn được gọi:

Ví dụ tương tự nhưng cho &=

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

Hơn nữa, JVM nên chạy đoạn mã trên như sau:

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

So sánh &&&kết quả

Là kết quả của các nhà khai thác &&&các tương tự khi áp dụng trên các giá trị boolean?

Hãy kiểm tra bằng cách sử dụng mã Java sau:

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

Đầu ra:

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

Do đó CÓ, chúng tôi có thể thay thế &&bằng &giá trị boolean ;-)

Vì vậy, tốt hơn sử dụng &=thay vì &&=.

Giống với ||=

Các lý do tương tự như &&=:
toán tử |=ít bị lỗi hơn ||=.

Nếu một nhà phát triển f2()không muốn bị gọi khi f1()trả hàng true, thì tôi khuyên bạn nên lựa chọn các lựa chọn thay thế sau:

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

hoặc là:

// 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
Xin chào @StriplingWarrior. Tôi đã kiểm tra với đồng nghiệp Yannick, chuyên gia Java giỏi nhất của chúng tôi. Tôi đã cập nhật câu trả lời của mình bằng cách sử dụng nguồn mã Java được sử dụng để kiểm tra điểm đó. Như bạn đã nói &&&cho kết quả tương tự. Cảm ơn bạn rất nhiều thông tin phản hồi của bạn. Bạn có thích câu trả lời của tôi không? Chúc mừng.
oHo

2
Nếu tôi muốn làm điều này rất nhanh thì sao? && = sẽ nhanh hơn & =, nếu nó tồn tại, vì vậy bạn nên sử dụng if (a) a = bđể tăng tốc độ
thám

Xin chào @adventurerOK. Xin lỗi, tôi không chắc hiểu ý bạn ... Tôi nghĩ a&=b;là nhanh hơn so với if(a) a=b;khi sử dụng các giá trị được lưu trữ trong thanh ghi CPU. Tuy nhiên, nếu bở trong bộ nhớ ngoài (không lưu trong bộ nhớ đệm), thì if(a) a=b;nhanh hơn. Ý bạn là gì? Vui lòng cung cấp thêm mã ví dụ ;-) Tôi tò mò về ý kiến ​​của bạn. Hẹn gặp lại. Chúc mừng
oHo

12
Tôi không đồng ý khi bạn nói "Và đây không phải là điều chúng tôi muốn." Nếu tôi viết, isOK &&= f2();tôi muốn nó ngắn mạch giống như &&vậy.
Tor Klingberg

3
Tôi không đồng ý với tuyên bố của bạn rằng các nhà khai thác sẽ dễ xảy ra lỗi hoặc vô dụng. Sử dụng các phép gán ghép là việc bạn làm để đi tắt đón đầu thông thường A = A op B, vì vậy mọi người hoàn toàn biết họ đang làm gì và có thể tự quan tâm đến các hàm ý. Nếu lý do của bạn thực sự là nguyên nhân cho sự vắng mặt của nó, tôi sẽ coi đó là sự bảo trợ không mong muốn. Tuy nhiên, tôi muốn cảm ơn bạn đã thêm dòng bool isOk = f1() || f2() || f3() || f4();vì đó là những gì tôi đã phải mù để nhìn thấy chính mình.
Franz B.

16

Có lẽ vì một cái gì đó như

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

Có vẻ như nó phải được gán cho xvà đánh giá someComplexExpression(), nhưng thực tế là đánh giá phụ thuộc vào giá trị của xkhông rõ ràng từ cú pháp.

Cũng bởi vì cú pháp của Java dựa trên C, và không ai thấy cần phải thêm các toán tử đó. Dù sao thì bạn có lẽ sẽ tốt hơn với câu lệnh if.


37
Tôi nghĩ đây không phải là một câu trả lời hay, bởi vì người ta có thể lập luận rằng có x() && y() vẻ như nên đánh giá cả hai mặt của biểu thức. Rõ ràng là mọi người chấp nhận rằng đó &&là đoản mạch, vì vậy điều đó cũng nên &&=làm theo.
polygenelubricants

2
@jleedev, đã đồng ý. Tôi tin rằng trong những tình huống này, điều quan trọng cần nhớ là điều này không tương đương với x = x && someComplexExpression () mà tương đương với x = someComplexExpression () && x. Đầu tiên phía bên phải sẽ / nên được đánh giá để phù hợp với mọi toán tử gán khác. Và với điều đó, && = sẽ không có hành vi nào khác với & =.
PSpeed

@polygene Đó là công bằng. Tuy nhiên, một cái gì đó trông như thế nào dựa trên những gì bạn đã quen thuộc và tôi đoán họ không muốn thêm bất cứ điều gì mới. Một điều tôi thích về C / C ++ / Java / C # là cú pháp cơ bản của các biểu thức gần giống nhau.
Josh Lee

1
@PSpeed, bạn đã nhầm. JLS rất rõ ràng về những gì mà phép gán ghép phải làm. Xem phần bổ sung của tôi ở trên.
polygenelubricants 24/02/10

1
@PSpeed: Trong trường hợp đó, a -= b;sẽ hoạt động hoàn toàn khác với a &&= b;.
David Thornley

10

Đó là cách này trong Java, bởi vì nó là cách này trong C.

Bây giờ câu hỏi tại sao nó lại như vậy trong C là bởi vì khi & và && trở thành các toán tử khác nhau (đôi khi trước khi C xuất phát từ B), các toán tử & = đơn giản bị bỏ qua.

Nhưng phần thứ hai của câu trả lời của tôi không có bất kỳ nguồn nào để sao lưu nó.


1
Về mặt hài hước - câu hỏi gần đây đã được hỏi trong diễn đàn C; và câu trả lời này đã được liên kết (mặc dù không được đánh dấu trùng lặp) Điều này làm cho hoàn thành đối số vòng tròn!
UKMonkey

5

Phần lớn là vì cú pháp Java dựa trên C (hoặc ít nhất là họ C), và trong C, tất cả các toán tử gán đó được biên dịch thành các lệnh hợp ngữ số học hoặc theo bit trên một thanh ghi duy nhất. Phiên bản toán tử gán tránh tạm thời và có thể đã tạo ra mã hiệu quả hơn trên các trình biên dịch không tối ưu hóa ban đầu. Toán tử logic (vì chúng được gọi trong C) các phép tương đương ( &&=||=) không có sự tương ứng rõ ràng như vậy với các lệnh hợp ngữ đơn lẻ; chúng thường mở rộng sang chuỗi hướng dẫn kiểm tra và nhánh.

Điều thú vị là các ngôn ngữ như ruby làm có || = và && =.

Chỉnh sửa: thuật ngữ khác nhau giữa Java và C


Tôi tin rằng ý bạn là các toán tử tương đương có điều kiện không có sự tương ứng rõ ràng như vậy. Trong thuật ngữ JLS, các toán tử logic boolean là &, |^; &&||là các toán tử điều kiện.
polygenelubricants

Trong thuật ngữ C, && và || là "toán tử logic", s6.5.13-14 trong ISO 9899: 1999. Các toán tử bitwise chỉ "logic" khi được áp dụng cho một bit (boolean trong java); không có kiểu bit đơn trong C và các toán tử logic ở đó áp dụng cho tất cả các kiểu vô hướng.
p00ya

trước C99, C thậm chí không có một boolloại. Đó là 20 năm trong lịch sử ngôn ngữ không có bool. Không có lý do gì để có &&=. hoạt động bitwise là đủ.
v.oddou

4

Một trong những mục tiêu ban đầu của Java là "Đơn giản, Hướng đối tượng và Quen thuộc." Như được áp dụng cho trường hợp này, & = quen thuộc (C, C ++ có nó và quen thuộc trong ngữ cảnh này có nghĩa là quen thuộc với một người biết hai điều đó).

&& = sẽ không quen thuộc và sẽ không đơn giản, theo nghĩa là các nhà thiết kế ngôn ngữ không tìm cách nghĩ về mọi toán tử mà họ có thể thêm vào ngôn ngữ, vì vậy càng ít toán tử thừa thì càng đơn giản.


3

Đối với vars Boolean, && và || sẽ sử dụng đánh giá ngắn mạch trong khi & và | không, vì vậy bạn sẽ mong đợi && = và || = cũng sử dụng đánh giá ngắn mạch. Có một trường hợp sử dụng tốt cho điều này. Đặc biệt nếu bạn đang lặp lại qua một vòng lặp, bạn muốn nhanh chóng, hiệu quả và ngắn gọn.

Thay vì viết

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

tôi muốn viết

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

và biết rằng một khi bVal là true, fn () sẽ không được gọi cho phần còn lại của các lần lặp.


1
Đối với vòng lặp đó, bạn có thể chỉ muốn làm if (fn(item)) { bVal = true; break; }.
Radiodef

2

' &' và ' &&' không giống như ' &&' là một hoạt động cắt ngắn sẽ không thực hiện nếu toán hạng đầu tiên là sai trong khi ' &' vẫn thực hiện điều đó (hoạt động với cả number và boolean).

Tôi đồng ý rằng nó có ý nghĩa hơn nếu tồn tại nhưng nó không phải là xấu nếu nó không ở đó. Tôi đoán nó không có ở đó vì C không có nó.

Thực sự không thể nghĩ ra tại sao.


0

Nó được cho phép trong Ruby.

Nếu tôi đoán, tôi sẽ nói rằng nó không được sử dụng thường xuyên nên nó không được triển khai. Một cách giải thích khác có thể là trình phân tích cú pháp chỉ nhìn vào ký tự trước dấu =


1
Java hỗ trợ << =, >> = và >>> =, vì vậy điều đó không hoàn toàn đúng.
Yishai

thật. Tôi không nghĩ về những thứ đó. Tôi đoán lời giải thích duy nhất sau đó là tần suất nó được sử dụng.
zzawaideh

0

Tôi không thể nghĩ ra lý do nào tốt hơn sau đó 'Nó trông xấu lạ thường!'


9
Nhưng khi đó C / Java không bao giờ có nghĩa là đẹp.
EFraim

1
Tôi hầu như không coi &&=là xấu xí
asgs

0

&

xác minh cả hai toán hạng, đó là một toán tử bitwise. Java định nghĩa một số toán tử bitwise, có thể được áp dụng cho các kiểu số nguyên, long, int, short, char và byte.

&&

ngừng đánh giá nếu toán hạng đầu tiên đánh giá là false vì kết quả sẽ là false, đó là một toán tử logic. Nó có thể được áp dụng cho boolean.

Toán tử && tương tự như toán tử &, nhưng có thể làm cho mã của bạn hiệu quả hơn một chút. Bởi vì cả hai biểu thức được so sánh bởi toán tử & phải đúng để toàn bộ biểu thức là đúng, không có lý do gì để đánh giá biểu thức thứ hai nếu biểu thức đầu tiên trả về sai. Toán tử & luôn đánh giá cả hai biểu thức. Toán tử && chỉ đánh giá biểu thức thứ hai nếu biểu thức đầu tiên là đúng.

Có toán tử gán && = sẽ không thực sự thêm chức năng mới cho ngôn ngữ. Số học của toán tử bitwise biểu cảm hơn nhiều, bạn có thể thực hiện phép tính theo từng bit nguyên, bao gồm cả số học Boolean. Các toán tử logic chỉ có thể thực hiện số học Boolean.


0

Brian Goetz (Kiến trúc sư ngôn ngữ Java tại Oracle) đã viết :

https://stackoverflow.com/q/2324549/ [câu hỏi này] cho thấy rằng có sự quan tâm đến việc có các toán tử này và không có lập luận rõ ràng tại sao chúng chưa tồn tại. Do đó, câu hỏi đặt ra là: Nhóm JDK đã thảo luận về việc thêm các toán tử này trong quá khứ chưa và nếu có thì lý do từ đâu lại không thêm chúng?

Tôi không biết bất kỳ cuộc thảo luận cụ thể nào về vấn đề cụ thể này, nhưng nếu ai đó đề xuất nó, câu trả lời có thể sẽ là: đó không phải là một yêu cầu vô lý, nhưng nó không có trọng lượng của nó.

"Sức nặng của nó" cần được đánh giá bằng chi phí và lợi ích cũng như tỷ lệ chi phí-lợi ích của nó so với các tính năng ứng viên khác.

Tôi nghĩ rằng bạn đang ngầm giả định (bằng cụm từ "có lãi") rằng chi phí gần bằng 0 và lợi ích lớn hơn 0, vì vậy nó có vẻ là một chiến thắng hiển nhiên. Nhưng điều này cho thấy sự hiểu biết không đúng về chi phí; một tính năng như thế này ảnh hưởng đến đặc tả ngôn ngữ, cách triển khai, JCK và mọi sách giáo khoa IDE và Java. Không có đặc điểm ngôn ngữ tầm thường. Và lợi ích, trong khi nonzero, là khá nhỏ.

Thứ hai, có vô số tính năng mà chúng tôi có thể làm, nhưng chúng tôi chỉ có khả năng thực hiện một vài tính năng sau mỗi vài năm (và người dùng có khả năng tiếp thu các tính năng mới rất hạn chế.) Vì vậy, chúng tôi phải rất cẩn thận khi lựa chọn, như mỗi tính năng (ngay cả một tính năng có vẻ tầm thường) tiêu tốn một phần ngân sách này và luôn lấy đi của nó từ những tính năng khác.
Đó không phải là "tại sao không phải là tính năng này", mà là "những tính năng nào khác mà chúng tôi sẽ không thực hiện (hoặc trì hoãn) để chúng tôi có thể thực hiện tính năng này, và đó có phải là một giao dịch tốt không?" Và tôi thực sự không thể tưởng tượng đây là một giao dịch tốt so với bất kỳ thứ gì khác mà chúng tôi đang làm.

Vì vậy, nó xóa bỏ thanh "không phải là một ý tưởng khủng khiếp" (vốn đã khá tốt, rất nhiều yêu cầu tính năng thậm chí không giải thích được điều đó), nhưng dường như không bao giờ xóa được thanh "sử dụng tốt hơn ngân sách phát triển Hơn bất cứ điều gì khác."


-1

a & b và a && b không giống nhau.

a && b là một biểu thức boolean trả về một boolean và a & b là một biểu thức bitwise trả về một int (nếu a và b là int).

bạn nghĩ chúng giống nhau ở điểm nào?


1
a & b nên không giống như a && b luôn đánh giá b.
Daniel Brückner
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.