2016-02-23 19 views
7

Trong một phương pháp tôi có điều này:Tại sao một biến cục bộ trong Java không được coi là "hiệu quả cuối cùng" mặc dù không có gì thay đổi nó sau đó?

int x = 0 
if (isA()) { 
    x = 1; 
} else if (isB()) { 
    x = 2; 
} 

if (x != 0) { 
    doLater(() -> showErrorMessage(x)); // compile error here 
} 

// no more reference to 'x' here 

Tôi không hiểu tại sao nó tạo ra lỗi biên dịch. Lỗi nói rằng x không phải là cuối cùng hoặc hiệu quả cuối cùng, do đó, nó không thể được truy cập từ cơ thể lambda. Không có sửa đổi đối với x sau cuộc gọi doLater, vì vậy giá trị của x thực sự đã được xác định khi được gọi là doLater.

Tôi đoán câu trả lời cho câu hỏi này là vì x không đủ điều kiện để được gọi là biến cuối cùng hiệu quả. Tuy nhiên, tôi muốn biết lý do là gì.

thể không trình biên dịch chỉ cần tạo một biến thức tạm thời, làm cho có hiệu quả chương trình như:

if (x != 0) { 
    final int final_x = x; 
    doLater(() -> showErrorMessage(final_x)); 
} 

và mọi thứ vẫn hoạt động giống nhau không?

+1

Tại sao bạn nghĩ rằng trình biên dịch sẽ có thể làm điều đó? – Tunaki

+1

Trình biên dịch có thể theo dõi vị trí cuối cùng một biến được thay đổi, ngoại trừ đó không phải là những gì nó làm. –

+0

@Tunaki bởi vì nó biết rằng x không được sửa đổi sau lambda, vì vậy nó luôn luôn có thể đảm bảo rằng x đã được cố định bởi thời gian nó được sử dụng. – yuku

Trả lời

15

Có hiệu lực cuối cùng có nghĩa là nó có thể đã được thực hiện final tức là nó không bao giờ thay đổi. Nó có nghĩa là hiệu quả, biến có thể là một biến số final.

Vấn đề là nó không theo dõi lần cuối cùng bạn thay đổi nó, nhưng đúng hơn, bạn đã bao giờ thay đổi nó chưa. Thay đổi tuyên bố if của bạn để

int x; 
if (isA()) { 
    x = 1; 
} else if (isB()) { 
    x = 2; 
} else { 
    x = 0; 
} 

hoặc

int x = isA() ? 1 : 
     isB() ? 2 : 0; 
+0

Có lẽ câu hỏi của tôi không chính xác. Những gì tôi tự hỏi là tại sao các đặc điểm kỹ thuật của một biến 'hiệu quả cuối cùng' không áp dụng cho mã mẫu của tôi? Giống như "tại sao quyết định của JLS không phải như vậy?". – yuku

+0

@yuku Biến cuối cùng hoặc trường có thể theo định nghĩa chỉ được gán chính xác một lần. Đối với một biến, thường là trong một initializer. Đối với một trường, đó thường là trong hàm tạo. Cụm từ khóa * được gán chính xác một lần *, đó là bài kiểm tra litmus cho final. * Hiệu quả cuối cùng * đơn giản có nghĩa là đó là hành động cuối cùng mà không được tuyên bố như vậy. Khi bạn khởi tạo 0, * sau đó * thay đổi thành 1 hoặc 2, nó không phải là cuối cùng trong bất kỳ ý nghĩa của từ. – Andreas

4

Biến số xcủa bạn sẽ là hiệu quả cuối cùng nó được khởi tạo một lần và không thay đổi lại trong bất kỳ trường hợp nào. Nếu bạn chỉ có:

int x = 0; 
doLater(() -> showErrorMessage(x)); 

thì nó sẽ được biên dịch.

Tuy nhiên, thêm điều kiện thể thay đổi giá trị của biến

int x = 0; 
if (isA()) { 
    x = 1; 
} else if (isB()) { 
    x = 2; 
} 

làm cho các biến là không có hiệu quả chính thức và do đó các lỗi biên dịch được tăng.


Bên cạnh đó, kể từ con trỏ phương pháp này bạn đã thực hiện sẽ không làm việc, bạn có thể cấu trúc lại mã của bạn một chút để một tuyên bố else if-đơn giản:

if (isA()) { 
    doLater(() -> showErrorMessage(1)); 
} else if (isB()) { 
    doLater(() -> showErrorMessage(2)); 
} 

và hoàn toàn thoát khỏi của x.

2

phiên bản ngắn, một biến là hiệu quả thức nếu nó là giao đúng một lần, bất kể đó mã con đường được thực thi.

phiên bản Long, trích dẫn Java Language Specification 4.12.4. final Variables (tôi nhấn mạnh):

Một số biến mà không phải là tuyên bố final đang thay vì coi hiệu quả thức:

  • Một biến cục bộ mà declarator có một initializer (§14.4.2) là hiệu quả cuối cùng nếu tất cả các follo cánh là đúng:
    • Nó không được khai báo final.
    • Nó không bao giờ xảy ra ở phía bên tay trái trong biểu thức bài tập (§15.26). (Lưu ý rằng người khai báo biến cục bộ có chứa bộ khởi tạo là không phải là một biểu thức gán.)
    • Nó không bao giờ xảy ra như toán hạng của toán tử tăng hoặc giảm (§15.14, §15.15).

Bây giờ, bạn có thể làm cho nó có hiệu quả thức bằng cách loại bỏ các initializer, bởi vì nó tiếp tục:

  • Một biến cục bộ mà declarator thiếu một initializerhiệu quả thức nếu tất cả các điều sau là đúng:
    • Nó không được khai báo final.
    • Bất cứ khi nào nó xảy ra ở phía bên tay trái trong biểu thức bài tập, chắc chắn nó chưa được gán và không được gán trước bài tập; có nghĩa là, nó chắc chắn là chưa được gán và không được chỉ định sau bên phải của biểu thức chuyển nhượng (§16 (Definite Assignment)).
    • Nó không bao giờ xảy ra dưới dạng toán hạng của tiền tố hoặc toán tử tăng hoặc giảm hậu tố.
Các vấn đề liên quan