Thực thi toán tử gán Java


76

Trong Java, tôi hiểu rằng phép gán đánh giá giá trị của toán hạng bên phải, vì vậy các câu lệnh như x == (y = x)eval to true.

Mã này, tuy nhiên, đầu ra false.

public static void main(String[]args){
    String x = "hello";
    String y = "goodbye";
    System.out.println(x.equals(x = y));
}

Tại sao thế này? Theo hiểu biết của tôi, đầu tiên nó đánh giá (x = y), chỉ định xgiá trị của y, và sau đó trả về giá trị của y. Sau đó x.equals(y)được đánh giá, nên truetừ đó xynên chia sẻ các tài liệu tham khảo giống nhau ngay bây giờ, nhưng thay vào đó, tôi nhận được false.

Ảnh chụp màn hình hiển thị nguồn và đầu ra là "sai"

Chuyện gì đang xảy ra ở đây?


13
Tôi nghĩ bạn muốn xem kết quả chox.equals( y = x )
nits.kk

1
Trình biên dịch có thể nội tuyến xy?
Lino

3
Bạn đang giả định rằng việc chuyển nhượng x = yở phía bên tay phải được thực hiện trước khi các xở phía bên tay trái được đánh giá?
khelwood

@khelwood vâng, đó là giả định của tôi. Nó không phải
Sam

1
@ nits.kk Tôi không nghĩ vậy. OP đã nói rằng họ hiểu rằng x == (y = x)đánh giá là đúng. Hành vi của những gì bạn đề nghị sau đó sẽ được rõ ràng ...
Pedro Một

Câu trả lời:


76

Trước hết: đó là một câu hỏi thú vị, nhưng không bao giờ nên đưa ra trong "mã thực", vì việc gán cho biến mà bạn gọi trong cùng một dòng sẽ gây nhầm lẫn ngay cả khi bạn biết nó hoạt động như thế nào.

Điều gì xảy ra ở đây là 3 bước sau:

  1. tìm ra đối tượng nào cần gọi phương thức (tức là đánh giá đối tượng đầu tiên x, điều này sẽ dẫn đến tham chiếu đến Chuỗi "hello")
  2. tìm ra các tham số (tức là đánh giá x = y, sẽ thay đổi xđể trỏ tới "tạm biệt" Chuỗi và cũng trả về một tham chiếu đến Chuỗi đó)
  3. gọi phương thức equalstrên kết quả của # 1 bằng cách sử dụng kết quả của # 2 làm tham số (sẽ là tham chiếu đến các Chuỗi tương ứng "hello" và "goodbye").

Nhìn vào mã byte được tạo cho phương thức đó sẽ làm rõ ràng (giả sử bạn thông thạo mã bytecode của Java):

     0: ldc           #2                  // String hello
     2: astore_1
     3: ldc           #3                  // String goodbye
     5: astore_2
     6: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
     9: aload_1
    10: aload_2
    11: dup
    12: astore_1
    13: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
    16: invokevirtual #6                  // Method java/io/PrintStream.println:(Z)V
    19: return

Dòng số 9 là bước 1 ở trên (tức là đánh giá xvà ghi nhớ giá trị).

Dòng # 10-12 là bước 2. Nó tải y, nhân bản nó (một lần để gán, một lần cho giá trị trả về của biểu thức gán) và gán nó cho x.

Dòng # 13 gọi equalskết quả được tính trong Dòng # 9 và kết quả của Dòng # 10-12.


36
TL; DR: x.equals(x = y)=> "hello".equals(x = y)=> "hello".equals(x = "goodbye")=> "hello".equals("goodbye")=> false.
Bernhard Barker

8
Một điểm quan trọng cần lưu ý là nó .có mức độ ưu tiên cao hơn= .
Gaurang Tandon

4
Đây là về thứ tự đánh giá hơn là về mức độ ưu tiên. Dấu ngoặc đơn làm cho mức độ ưu tiên không liên quan.
amalloy

38

Câu hỏi hay! Và JLS có câu trả lời ...

§15.12.4.1 (Ví dụ 15.12.4.1-2). Thứ tự đánh giá trong khi mời phương pháp:

Là một phần của lời gọi phương thức thể hiện, có một biểu thức biểu thị đối tượng được gọi. Biểu thức này dường như được đánh giá đầy đủ trước khi bất kỳ phần nào của bất kỳ biểu thức đối số nào đối với lệnh gọi phương thức được đánh giá.

Vì vậy, trong:

String x = "hello";
String y = "goodbye";
System.out.println(x.equals(x = y));

sự xuất hiện của xtrước .equalsđược đánh giá trước, trước biểu thức đối số x = y.

Do đó, một tham chiếu đến chuỗi   hello  được nhớ là tham chiếu đích trước khi biến cục bộ xđược thay đổi để tham chiếu đến chuỗi goodbye. Do đó, equalsphương thức được gọi cho đối tượng đích hellocó đối số goodbye, vì vậy kết quả của lời gọi là false.


28

Điều quan trọng cần nhớ là a Stringtrong java là một đối tượng và do đó là một tham chiếu. Khi bạn gọi

x.equals(...)

Nó đang kiểm tra xem giá trị tại vị trí hiện đang được tham chiếu xcó bằng với giá trị bạn đang chuyển vào hay không. Bên trong, bạn đang thay đổi giá trị xđang tham chiếu , nhưng bạn vẫn đang gọi equalsvới tham chiếu ban đầu (tham chiếu tới "hello"). Vì vậy, ngay bây giờ mã của bạn đang so sánh để xem liệu "xin chào" có bằng "tạm biệt" hay không, rõ ràng là không. Sau thời điểm này, nếu bạn sử dụng xlại, nó sẽ dẫn đến một tham chiếu đến cùng giá trị với y.


nếu tôi hiểu bạn, điều này: "hello".equals((x = y))nên quay lại true?
Halayem Anis

3
Không, bởi vì (x = y) sẽ trả về giá trị của y, đó là "tạm biệt". Vì vậy, nếu bạn đã làm "tạm biệt" Equals (x = y), điều đó sẽ trở thành sự thật
Keveloper

5

x=ytrong ngoặc đơn có nghĩa là biểu thức (x=y)bây giờ là goodbye, trong khi x bên ngoài x.equalsgiữ giá trịhello


2
Điều này thực sự không giải thích tại sao điều này lại xảy ra và nó cũng không cung cấp thêm thông tin chi tiết hữu ích cho những người khác.
Grey

Bây giờ tôi đọc nó out loud, tôi có xu hướng đồng ý với you..the câu trả lời khác là khá tiết tho nên dint chỉnh sửa nó ..
Chetan Jadhav CD

4

Reimus đã đưa ra câu trả lời chính xác, nhưng tôi muốn nói rõ hơn.

Trong Java (và hầu hết các ngôn ngữ), quy ước là biến ở bên trái, gán ở bên phải.

Hãy chia nhỏ nó:

String x = "hello";
//x <- "hello"

String y = "goodbye";
//y <- "goodbye";

Đối với mục đích gỡ lỗi cũng như khả năng đọc mã, bạn nên chia nhỏ các dòng để chúng chỉ làm một việc.

System.out.println(x.equals(x = y)); //Compound statement

Ở đây, x.equals(...)được gọi trên tham chiếu ban đầu tới x, hoặc "xin chào", nó được cập nhật cho tham chiếu thứ hai.

Tôi sẽ viết điều này là (và điều này sẽ cung cấp cho bạn câu trả lời mong đợi của bạn):

x = y;
// x <- y = "goodbye"

boolean xEqualsX = x.equals(x);
// xEqualsX <- true

System.out.println(xEqualsX);
// "true"

Bây giờ điều này có vẻ rõ ràng rằng nó nên hoạt động theo cách này, nhưng cũng thực sự dễ dàng để xem chính xác những gì đang diễn ra trong mỗi dòng, đó là điều bạn nên cố gắng.


2
Re: "always": có một thứ như quá rõ ràng: bạn có muốn System.out.println("Bytes: "+1024*k);viết nó thành ba câu không?
Davis Herring

@DavisHerring điều này nằm trong ngữ cảnh gỡ lỗi. Nếu bạn đang cố gắng gỡ lỗi câu lệnh đó, thì có, tôi thực sự khuyên bạn nên chia câu lệnh này thành các thành phần của nó. Một câu lệnh cho phép nhân và một câu lệnh khác cho phép in. Nó cho phép sự linh hoạt tối đa. Theo nguyên tắc chung, bạn muốn mỗi dòng chỉ làm một việc. Nó làm cho mã dễ đọc hơn và có thể gỡ lỗi dễ dàng hơn.
Ngẫu nhiên Slayer

2

Tôi đã thử câu hỏi của bạn trong eclipse cả hai biểu thức của bạn đều đúng. 1) x == (y = x) đánh giá là true thì đúng vì giá trị của x gán cho y là 'xin chào' thì x và y so sánh chúng sẽ giống nhau nên kết quả sẽ đúng

2) x.equal (x = y) sai vì giá trị của y gán cho x là tạm biệt thì x và x so sánh giá trị của chúng sẽ khác nhau nên kết quả sẽ sai


1

Tôi thấy câu hỏi trong thuật ngữ giáo dân là "hello".equals("goodbye"). Vì vậy, nó trả về false.


1

Trong java String là một lớp.

String x = "hello";
String y = "goodbye"; 

là hai Chuỗi khác nhau đề cập đến hai giá trị khác nhau không giống nhau và nếu bạn so sánh

 System.out.println(x.equals(x = y)); 
//this compare value (hello and goodbye) return true

    System.out.println(x == (y = x)); 
// this compare reference of an object (x and y) return false  

1
Bạn đã thực sự chạy mã mẫu này chưa? Như câu hỏi đã nêu, System.out.println(x.equals(x = y));trả về false, trái với những gì câu trả lời của bạn tuyên bố.
Charlie Harding

-4

Nó đang xem nếu x.equals (gán x cho y, trả về true luôn luôn) vì vậy về cơ bản x.equals (true)


8
điều này không đúng sự thật, đó không phải là cách đánh giá bài tập
Sam
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.