2013-04-04 24 views
35

này thất bại trong việc biên dịch (với một lỗi illegal forward reference), như người ta mong đợi:Trình khởi chạy đệ quy hoạt động khi tôi thêm "cái này"?

class test { 
    int x = x + 42; 
} 

Nhưng công trình này:

class test { 
    int x = this.x + 42; 
} 

gì đang xảy ra? Những gì được chỉ định trong trường hợp sau?

+10

Làm thế nào lạ ... (+1) – NPE

+4

Nhắc tôi: trong khi (đúng) {try {return; } cuối cùng {tiếp tục; }} – devconsole

Trả lời

17

Tóm tắt: Cả hai trình khởi tạo truy cập một trường chưa được khởi tạo (và do đó vẫn có giá trị mặc định bằng 0). Vì đây có thể là lỗi lập trình, ngôn ngữ này cấm một số dạng đơn giản như vậy. Tuy nhiên, nó không cấm hình thức phức tạp hơn.

Hành vi này là phù hợp với JLS, cụ thể §8.3.2.3. Restrictions on the use of Fields during Initialization

Việc kê khai của một thành viên cần phải xuất hiện textually trước khi nó được sử dụng chỉ khi các thành viên là một thể hiện (tương ứng static) lĩnh vực của một lớp hoặc giao diện C và tất cả các điều kiện sau đây giữ:

  • việc sử dụng xảy ra trong một thể hiện (tương ứng static) initializer biến của C hoặc trong một thể hiện (tương ứng static) initializer của C.

  • Cách sử dụng không nằm ở phía bên tay trái của bài tập.

  • Cách sử dụng là thông qua một tên đơn giản.

  • C là lớp hoặc giao diện trong cùng bao quanh việc sử dụng.

Ví dụ đầu tiên đáp ứng tất cả bốn điều kiện và do đó không hợp lệ. Ví dụ thứ hai không thỏa mãn điều kiện thứ ba (this.x không phải là một tên đơn giản), và do đó là OK.

Trình tự chung của các sự kiện như sau:

  • Khi một thể hiện của một lớp được tạo ra, tất cả các trường được khởi tạo giá trị mặc định kiểu của họ.
  • Initializers are then run in textual order (từ trên xuống dưới).

Do đó nếu trình khởi tạo đề cập đến trường xuất hiện sau này trong định nghĩa lớp (hoặc chính trường), nó sẽ thấy giá trị mặc định của trường khác. Đây có thể là lỗi lập trình và do đó bị cấm bởi §8.3.2.3.

Nếu bạn phá vỡ §8.3.2.3, ví dụ: sử dụng this. để chuyển tiếp tham chiếu đến một trường, you'll see the default value (số không cho int). Vì vậy sau đây là rõ ràng và được đảm bảo để thiết x-42:

class test { 
    int x = this.x + 42; 
} 
+0

@nneonneo: Tôi nghĩ rằng tôi đã tìm ra logic đằng sau điều này. Vui lòng xem câu trả lời cập nhật của tôi. – NPE

18

Đó là quá khó khăn để phát hiện và ngăn cấm tất cả các truy cập vào x trong quá trình khởi x.Ví dụ:

int x = that().x;    | int x = getX(); 
           | 
Test that(){ return this; }  | int getX(){ return x; } 

Đặc điểm dừng tại "truy cập theo tên đơn giản" và không cố gắng toàn diện hơn.

Trong phần khác, "Phân định xác định", thông số kỹ thuật thực hiện điều tương tự. Ví dụ

public class Test 
{ 
    static final int y; 
    static final int z = y; // fail, y is not definitely assigned 
    static{ y = 1; } 
} 

public class Test 
{ 
    static final int y; 
    static final int z = Test.y; // pass... because it's not a simple name 
    static{ y = 1; } 
} 

Điều thú vị là, "Phân Definite" đặc biệt đề cập đến rằng this.x tương đương với x

(hoặc, đối với một lĩnh vực, tên đơn giản của trường đủ điều kiện của thành viên này)

khoản này cũng có thể được thêm vào phần được NPE trích dẫn.

  • việc sử dụng là thông qua một tên đơn giản (hoặc một tên đơn giản đủ điều kiện của thành viên này)

Nhưng cuối cùng, nó là không thể tại thời gian biên dịch để phân tích tất cả các tập quán tốt/truy cập đến một trường.

+0

Bạn có chế độ xem liệu 'int x = this.x + 42;' có ngữ nghĩa được xác định rõ không? – NPE

+0

thông số có lẽ cũng nên cấm nó, như "việc sử dụng là thông qua một tên đơn giản hoặc một tên đơn giản đủ điều kiện bởi điều này" – ZhongYu

1

Trong trường hợp trình biên dịch đầu tiên cố gắng đánh giá biểu thức 'x + 42' nhưng không thành công vì x không được khởi tạo.

Trong biểu thức trường hợp thứ hai 'this.x + 42' được đánh giá khi chạy (vì từ khóa 'this'), khi x đã được khởi tạo và có giá trị 0.

Các vấn đề liên quan