2013-01-18 39 views
15

Tôi biết rằng mã này:Java đôi khởi tạo công trình luôn luôn?

Set<String> set = new HashSet<String>() {{ 
    add("test1"); 
    add("test2"); 
}}; 

thực sự là:

Set<String> set = new HashSet<String>() { 
    {//initializer 
    add("test1"); 
    add("test2"); 
    } 
}; 

Khối initializer đang được thực hiện trước khi khối nhà xây dựng. Trong ví dụ trên, thêm ("test1") được gọi trước khi hàm tạo đang được thi hành. Hàm khởi tạo có thể khởi tạo nhiều trường thể hiện để lớp này hoạt động. Tôi tự hỏi tại sao gọi .add() trước khi hàm tạo sẽ hoạt động? Có trường hợp nào gây ra vấn đề không?

+2

Điều này có thể sẽ rơi vào 'hành vi không xác định' ... – 11684

+2

Câu hỏi thú vị. Tôi không thực sự có một câu trả lời nhưng tôi nghĩ rằng bạn đang làm một giả định không chính xác ở đây. Nếu bạn nhìn vào hàm tạo của HashSet nó thực hiện điều này: 'map = new HashMap ();' và phương thức add thực hiện điều này: 'return map.put (e, PRESENT) == null;'. Nếu giả định của bạn là chính xác, điều này sẽ gây ra một NPE. –

+0

[Chắc chắn không phải là sai khi nghĩ về việc liệu "mẫu" này thực sự đáng giá hay không] (http://stackoverflow.com/q/924285/521799) –

Trả lời

17

Có một chi tiết bạn đã bỏ qua để giải thích điều này.

Trước hết, chúng ta hãy xem xét các bước từ 3 đến 5 của initialization procedure (tóm tắt):

3. constructor lớp cha được gọi là
4. initializers dụ được gọi là
5. cơ thể của các nhà xây dựng được gọi là

Chi tiết bạn đã bỏ qua là biểu thức của bạn không chỉ đơn giản là tạo ra một thể hiện mới của lớp HashSet, thực tế là tạo ra một thể hiện mới của một phân lớp ẩn danh là HashSet. (Tôi tin rằng điều này được chỉ định trong section 15.9.1.)

Vì bạn không khai báo hàm tạo nên hàm tạo mặc định được sử dụng. Nhưng trước đó, hàm tạo của siêu lớp HashSet đã hoàn thành.

Vì vậy, tóm lại, hàm tạo HashSet hoàn thành trước khi khối khởi tạo của bạn chạy.

+0

Cảm ơn, Samuel. Bạn đã giải thích chính xác mảnh bị thiếu. Khối mã đó không khởi tạo HashSet, mà là một lớp con ẩn danh của HashSet. – user926958

+0

@ user926958, đó là một chi tiết dễ dàng để bỏ lỡ trong ví dụ này. Nếu bạn bắt nguồn từ một giao diện như 'Set' hoặc một lớp trừu tượng, bạn sẽ được yêu cầu thêm một số định nghĩa phương thức để làm cho nó rõ ràng hơn một lớp mới đang được định nghĩa ở đây. –

6

Giả định này là sai:

Khối initializer đang được thực hiện trước khi khối nhà xây dựng.

Bởi vì trong trường hợp đặc biệt này, khối initializer là một phần của khối nhà xây dựng.

Tình trạng docs

Java khối bản sao biên dịch initializer vào mỗi constructor. Do đó, cách tiếp cận này có thể được sử dụng để chia sẻ một khối mã giữa nhiều hàm tạo.

Tôi nghĩ bạn đang bối rối với trình khởi chạy tĩnh.

+0

Hãy xem nhận xét của tôi về câu hỏi gốc. Không phải là khối phải xảy ra sau mỗi constructor thay vì trước khi nó? Không phải là bạn đang nói khác, tôi chỉ muốn hiểu rõ hơn. –

+0

Thứ tự giống như được khởi tạo trước khi hàm tạo, ít nhất tôi nhận được nó từ đây: http://stackoverflow.com/questions/2007666/in-what-order-do-static-initializer-blocks-in-java-run – user926958

+1

Tôi không có ý là thô lỗ nhưng đây không phải là những gì đang xảy ra; khối initalizer được gọi sau siêu nhưng trước phần còn lại của hàm tạo sẽ gây ra vấn đề trong hầu hết các trường hợp; ông đang làm trong một phần mở rộng nội tuyến của lớp HashMap đó là lý do tại sao điều này luôn luôn hoạt động. –

4

Trình khởi tạo sơ thẩm được thực thi ngay sau khi đối tượng được tạo. Về cơ bản bạn đang tạo một phần mở rộng nội tuyến của HashSet và sau đó "ngay sau" nó được tạo ra, bạn đang thêm hai mục vào nó.

Đây là mẫu sử dụng phổ biến trong các đối tượng giả để thử nghiệm, chẳng hạn như trong JMock, nhưng cũng có các cách sử dụng tiện dụng khác.

Hy vọng điều này sẽ hữu ích.

+2

Nó không được thực hiện sau khi đối tượng được xây dựng. Nó được thực thi trong quá trình xây dựng đối tượng, giữa hàm tạo lớp siêu và phần thân của hàm tạo. –

3

Tôi coi đây là thực tiễn không tốt vì nó tạo ra các lớp con vô nghĩa có thể ảnh hưởng đến việc sử dụng bộ nhớ và hiệu suất của ứng dụng. Dù sao, chương trình là chính xác bởi vì các constructor superclass được gọi trước khi khởi tạo thể hiện. Vì vậy, khi bộ khởi tạo của bạn được chạy, hàm tạo HashSet đã chạy để cuộc gọi đến add sẽ hoạt động.

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