2010-03-10 8 views
24

Tôi có thể thấy rằng Collections.unmodifiableSet trả lại chế độ xem không thể sửa đổi của tập hợp đã cho nhưng tôi không hiểu tại sao chúng tôi không thể chỉ sử dụng công cụ sửa đổi final để thực hiện việc này.Collections.unmodifiableSet() làm gì trong Java?

Theo hiểu biết của tôi, final tuyên bố một hằng số: một thứ không thể sửa đổi được. Vì vậy, nếu một tập hợp được khai báo là một hằng số thì nó không thể được sửa đổi: không có gì có thể được gỡ bỏ khỏi tập và không có gì có thể được thêm vào.

Tại sao chúng ta cần Collections.unmodifiableSet?

Trả lời

51

final tuyên bố tham chiếu đối tượng không thể sửa đổi, ví dụ:

private final Foo something = new Foo(); 

tạo mới Foo và đặt tham chiếu trong something.Sau đó, bạn không thể thay đổi something để trỏ đến một trường hợp khác là Foo.

Điều này làm không ngăn không cho sửa đổi trạng thái bên trong của đối tượng. Tôi vẫn có thể gọi bất kỳ phương thức nào trên Foo có thể truy cập vào phạm vi có liên quan. Nếu một hoặc nhiều phương thức đó sửa đổi trạng thái bên trong của đối tượng đó, thì final sẽ không ngăn điều đó.

Như vậy, sau đây:

private final Set<String> fixed = new HashSet<String>(); 

không không tạo Set mà không thể được thêm vào hoặc thay đổi; nó chỉ có nghĩa là fixed sẽ chỉ bao giờ tham chiếu đến trường hợp đó.

Ngược lại, thực hiện:

private Set<String> fixed = Collections.unmodifiableSet(new HashSet<String>()); 

tạo ra một thể hiện của một Set mà sẽ ném UnsupportedOperationException nếu một cố gắng gọi fixed.add() hoặc fixed.remove(), ví dụ - đối tượng chính nó sẽ bảo vệ nhà nước nội bộ của mình và ngăn chặn nó từ đang được sửa đổi.

Đối với đầy đủ lợi ích:

private final Set<String> fixed = Collections.unmodifiableSet(new HashSet<String>()); 

tạo ra một thể hiện của một Set mà sẽ không cho phép tình trạng nội bộ của mình phải được thay đổi, và cũng có nghĩa là fixed sẽ chỉ bao giờ trỏ đến một thể hiện của bộ đó.

Lý do có thể sử dụng final để tạo các hằng số nguyên thủy dựa trên thực tế là không thể thay đổi giá trị. Hãy nhớ rằng fixed ở trên chỉ là một tham chiếu - một biến chứa địa chỉ không thể thay đổi được. Vâng, đối với nguyên thủy, ví dụ:

private final int ANSWER = 42; 

giá trị của ANSWER là 42. Kể từ ANSWER không thể thay đổi, nó sẽ chỉ bao giờ có giá trị 42.

Một ví dụ mà làm mờ tất cả các dòng sẽ là:

private final String QUESTION = "The ultimate question"; 

Theo các quy tắc trên, QUESTION chứa địa chỉ của phiên bản String đại diện cho "Câu hỏi cuối cùng" và địa chỉ đó không thể thay đổi được. Điều cần ghi nhớ ở đây là chính bản thân bạn không thể làm bất cứ điều gì với một phiên bản String thay đổi nó và bất kỳ hoạt động nào có thể làm như vậy (chẳng hạn như replace, substring, v.v.) các phiên bản của String.

14

final chỉ đảm bảo rằng tài liệu tham khảo cho đối tượng biến đại diện không thể thay đổi nó không làm bất cứ điều gì cho trường hợp đối tượng và tính đột biến của đối tượng.

final Set s = new Set(); chỉ đảm bảo bạn không thể thực hiện lại s = new Set();. Nó không làm cho các thiết lập unmodifiable, nó nếu bạn đã không thể thêm bất cứ điều gì để nó bắt đầu với. Vì vậy, để làm cho nó thực sự rõ ràng, final chỉ ảnh hưởng đến biến tham chiếu không phải là đối tượng mà điểm tham chiếu đến.

tôi có thể làm như sau:

final List<String> l = new ArrayList<String>(); 
l.add("hello"); 
l.add("world"); 
l.remove(0); 

nhưng tôi không thể làm điều này.

l = new ArrayList<String>(); 

một lần nữa vì final Tôi không thể sửa đổi biến mà l trỏ đến.

bạn phải thực hiện một trong ba điều sau để tạo chuỗi bộ chứa Bộ sưu tập an toàn.

java.util.Collections.syncronizedXXX(); 

hoặc

java.util.Collections.unmodifiableXXX(); 

hoặc sử dụng một trong những container thích hợp từ java.util.concurrency.* package.

nếu tôi có đối tượng Person và đã làm final Person p = new Person("me"); điều đó có nghĩa là tôi không thể chỉ định lại p để trỏ đến đối tượng Person khác. Tôi vẫn có thể làm p.setFirstName("you");

What confuses tình hình là

final int PI = 3.14; 
final String greeting = "Hello World!"; 

nhìn như const trong C++, trong khi thực tế các đối tượng mà chúng trỏ tới là không thay đổi/unmodifiable bởi thiên nhiên. Thùng chứa hoặc đối tượng có phương pháp thay đổi có thể thay đổi trạng thái bên trong của đối tượng không phải là const chỉ tham chiếu cho các đối tượng đó là final và không thể được gán lại cho tham chiếu một đối tượng khác.

+1

Bài đăng hay. Tóm lại, _reference_ không thể thay đổi nhưng _contents_ của đối tượng * có thể *. – extraneon

3

final không phải là (C++ - kiểu) const. Không giống như C++, Java không có const -methods hoặc bất kỳ thứ gì tương tự, và các phương thức có thể thay đổi đối tượng có thể được gọi thông qua tham chiếu final.

Collections.unmodifiable* là trình bao bọc thực thi (chỉ trong thời gian chạy, không phải lúc biên dịch) chỉ đọc cho bộ sưu tập có liên quan.

0

Collections.unmodifiableSet(Set<? extends T>) sẽ tạo trình bao bọc trên tập hợp gốc. Không thể sửa đổi bộ trình bao bọc này. nhưng vẫn có thể sửa đổi bộ gốc.

Ví dụ:

Set<String> actualSet=new HashSet<String>(); //Creating set 

Thêm một số yếu tố

actualSet.add("aaa"); 
actualSet.add("bbb"); 

In ấn thêm yếu tố

System.out.println(actualSet); //[aaa, bbb] 

Đặt actualSet vào bộ unmodifiable và gán cho tham chiếu mới (wrapperSet).

Set<String> wrapperSet=Collections.unmodifiableSet(orginalSet); 

In trình bao bọcSet. vì vậy nó sẽ có giá trị actualSet

System.out.println(wrapperSet); //[aaa, bbb] 

phép thử để loại bỏ/thêm một yếu tố trên wrapperSet.

wrapperSet.remove("aaa"); //UnSupportedOperationException 

Thêm yếu tố thêm một trong actualSet

actualSet .add("ccc"); 

In actualSetwrapperSet. cả hai giá trị đặt đều giống nhau. Vì vậy, nếu bạn thêm/xóa bất kỳ phần tử nào trên tập hợp thực tế, các thay đổi cũng sẽ được phản ánh trên bộ trình bao bọc.

System.out.println(actualSet); //[aaa, ccc, bbb] 
    System.out.println(wrapperSet); // [aaa, ccc, bbb] 

Cách sử dụng:

Collections.unmodifiableSet(Set<? extends T>) này được sử dụng để ngăn chặn biến đổi của phương pháp getter Set của bất kỳ đối tượng. giả sử

public class Department{ 

    private Set<User> users=new HashSet<User>(); 

    public Set<User> getUsers(){ 
     return Collections.unmodifiableSet(users); 
    } 
}