2013-06-22 61 views
16

Khi khai báo một lớp bên trong cục bộ bên trong một phương thức, tại sao nó lại hợp pháp để bao gồm các chuỗi tĩnh cuối cùng hoặc int nhưng không hợp pháp để bao gồm các đối tượng khác?Khai báo tĩnh Java cuối cùng trong phương thức lớp địa phương

Ví dụ:

class Outer { 
void aMethod() { 
    class Inner { 
     final static String name = "compiles"; 
     final static int ctr = 10; // compiles 
     final static Integer intThree = Integer.valueOf(3); // does not compile! 
     final static obj objConst = new Object(); // does not compile! 
    } 

    Inner inner = new Inner(); 
} 
} 

Khi tôi biên dịch này, tôi nhận được như sau:

InnerExample.java:6: inner classes cannot have static declarations 
     final static Integer outer = Integer.valueOf(3); 
          ^
InnerExample.java:7: inner classes cannot have static declarations 
     final static Object objConst = new Object(); 
          ^

Tại sao sự khác biệt? Có phải vì String không thay đổi? Nếu vậy, sẽ không Integer.valueOf() cũng có giá trị?

+3

Tôi khá chắc chắn đó là vì "biên dịch" và 10 là biểu thức hằng số biên dịch, nhưng tôi chưa tìm thấy quy tắc JLS xung quanh điều này. –

Trả lời

17

Đó là vì hai thành viên tĩnh đầu tiên được gán cho hằng số biên dịch của kiểu nguyên thủy hoặc kiểu chuỗi.

Từ Java Language Specification, section 8.1.3:

8.1.3. Các lớp bên trong và các trường hợp bao vây

Các lớp bên trong có thể không khai báo các thành viên tĩnh, trừ khi chúng là các biến không đổi (§4.12.4) hoặc xảy ra lỗi biên dịch.

Và từ 4.12.4:

Một biến kiểu nguyên thủy hoặc kiểu String, đó là cuối cùng và khởi tạo với một biểu thức hằng thời gian biên dịch (§15.28), được gọi là một biến liên tục.

EDIT:

tôi thấy ngạc nhiên này lúc đầu. Suy nghĩ về nó nhiều hơn, một lợi thế cho giới hạn này là không cần phải lo lắng về khi các thành viên tĩnh của các lớp bên trong được khởi tạo. Bạn có thể di chuyển một lớp bên trong xung quanh trong lớp chứa của nó, mà không lo ngại rằng các giá trị của các thành viên tĩnh của nó sẽ bị thay đổi.

3

xem xét định nghĩa của một thời gian biên dịch thường xuyên biểu từ 15.28:

Một biểu thức hằng số thời gian biên dịch là một biểu thức biểu thị một giá trị nguyên thủy loại hoặc một String mà không hoàn thành đột ngột và bao gồm chỉ sử dụng như sau:

  • literals kiểu nguyên thủy và literals kiểu String (§3.10.1, §3.10.2, §3.10.3, §3.10.4, §3.10.5)
  • phôi để loại nguyên thủy và phôi gõ String (§15.16)
  • Các nhà khai thác unary +, -, ~, và ! (nhưng không ++ hoặc --) (§15.15.3, §15.15.4, §15.15.5, §15.15.6)
  • Các toán tử nhân số *, /% (§15.17)
  • Các nhà khai thác phụ +- (§15.18)
  • Các nhà khai thác dịch chuyển <<, >>, và >>> (§15.19)
  • Các toán tử quan hệ <, <=, >>= (nhưng không instanceof) (§15.20)
  • Các nhà khai thác bình đẳng ==!= (§15.21)
  • Các Bitwise và khai thác hợp lý &, 012.và | (§15.22)
  • Các điều kiện và điều hành && và có điều kiện-hoặc điều hành || (§15.23, §15.24)
  • Các nhà điều hành có điều kiện ternary ? : (§15.25)
  • biểu thức trong ngoặc đơn (§ 15.8.5) có biểu thức được chứa là một biểu thức không đổi.
  • Tên đơn giản (§6.5.6.1) tham chiếu đến các biến không đổi (§4.12.4).
  • Tên đủ điều kiện (§6.5.6.2) của biểu mẫu LoạiName. Mã định danh tham chiếu đến hằng số biến (§4.12.4).

Tiếp theo từ định nghĩa của một thời gian biên dịch thường xuyên biểu, chúng tôi có 4.12.4:

Một biến kiểu nguyên thủy hoặc gõ String, đó là final và khởi tạo với một compile- biểu thức hằng số thời gian (§15.28), được gọi là biến cố định.

Cuối cùng, từ 8.1.3:

lớp Inner chưa được công bố thành viên tĩnh, trừ khi họ là liên tục biến (§4.12.4), hoặc một thời gian biên dịch lỗi xảy ra.

5

Tìm hiểu thêm về câu trả lời trước. Giá trị được gán phải được trình biên dịch chứng minh rằng nó là một hằng số. Trình biên dịch Java biết ngữ nghĩa của các kiểu cơ sở (int, float, vv) và lớp java.lang.String, nhưng không phải các lớp khác. Điều này có thể hiểu được sự liên tục của hai ví dụ đầu tiên. Trình biên dịch không hiểu rằng Integer.valueOf (3) cũng là (hiệu quả) một hằng số (thực sự không phải là hằng số, nhưng luôn luôn giống nhau) giá trị mặc dù một con người, biết cách lớp Integer hoạt động, không biết rằng . Trình biên dịch xử lý điều này như thể nó là Integer.valueOf (x) có thể thay đổi. Sẽ tốt hơn nếu Java cung cấp chú thích, chẳng hạn như @interface Consistent, tuyên bố rằng hành vi của phương thức là ổn định đối với bất kỳ tham số nào, chẳng hạn như:

Trong lớp Integer: @Không công khai Giá trị nguyên tốOf (int x) { ...}

tĩnh cuối cùng Integer intThree = Integer.valueOf (3); // hiện đang biên dịch!

Điều đó cho biết phương thức trả về cùng một đối tượng hoặc cùng một đối tượng trên mỗi lệnh gọi được cung cấp cùng các giá trị đối số. Vì đối số là một biểu thức không đổi, trình biên dịch có thể suy ra rằng kết quả sẽ giống nhau/bằng nhau trong mọi cách sử dụng và do đó có thể được coi là một hằng số. Trong trường hợp này, Integer trả về cùng một đối tượng, nhưng nó có thể trả về một đối tượng khác (nhưng bằng nhau) cho nhiều giá trị đầu vào của lager (nghĩa là nó lưu trữ các giá trị gần 0).

Lưu ý rằng "mới" luôn trả về một đối tượng khác. Đối với Object mới() nó luôn là một đối tượng không bằng bất kỳ đối tượng nào khác.

+0

Đây là một lời giải thích tuyệt vời và giúp trả lời phần thứ hai, đó là cách hiểu hoạt động của Integer.valueOf() liên quan đến các hằng số biên dịch thời gian. –