Làm thế nào để tạo các đối tượng bất biến trong Java?


83

Làm thế nào để tạo các đối tượng bất biến trong Java?

Những đối tượng nào nên được gọi là bất biến?

Nếu tôi có lớp với tất cả các thành viên tĩnh thì nó có phải là bất biến không?


bản sao có thể xảy ra của Immutable nghĩa là gì?
Joachim Sauer

1
Câu hỏi được liên kết ở trên không giống nhau, nhưng câu trả lời của câu hỏi đó sẽ trả lời tất cả các nhiệm vụ của bạn.
Joachim Sauer

Nếu lớp của bạn là tất cả các thành viên tĩnh, nó không có trạng thái (không có cá thể nào có trạng thái riêng lẻ) và câu hỏi về khả năng thay đổi hoặc bất biến sẽ trở thành tranh luận.
Sebastian Redl

có cách nào khác để khởi tạo các trường khác với hàm tạo không. Tôi có hơn 20 lĩnh vực trong lớp của mình. Rất khó để khởi tạo tất cả các trường bằng cách sử dụng hàm tạo, một số trường thậm chí còn là tùy chọn.
Nikhil Mishra

Câu trả lời:


88

Dưới đây là các yêu cầu khó của một đối tượng bất biến.

  1. Kết thúc lớp học
  2. tạo tất cả các thành viên cuối cùng, đặt chúng một cách rõ ràng, trong một khối tĩnh hoặc trong phương thức khởi tạo
  3. Đặt tất cả thành viên ở chế độ riêng tư
  4. Không có phương pháp nào sửa đổi trạng thái
  5. Hãy cực kỳ cẩn thận để giới hạn quyền truy cập vào các thành viên có thể thay đổi (hãy nhớ trường có thể có finalnhưng đối tượng vẫn có thể thay đổi được. Tức là private final Date imStillMutable). Bạn nên thực hiện defensive copiestrong những trường hợp này.

Lý do đằng sau việc tạo ra lớp học finallà rất tinh vi và thường bị bỏ qua. Nếu những người không phải là cuối cùng của nó có thể tự do mở rộng lớp, ghi đè publichoặc protectedhành vi của bạn, thêm các thuộc tính có thể thay đổi, sau đó cung cấp lớp con của họ như một lớp thay thế. Bằng cách khai báo lớp, finalbạn có thể đảm bảo điều này sẽ không xảy ra.

Để xem vấn đề đang diễn ra, hãy xem xét ví dụ dưới đây:

public class MyApp{

    /**
     * @param args
     */
    public static void main(String[] args){

        System.out.println("Hello World!");

        OhNoMutable mutable = new OhNoMutable(1, 2);
        ImSoImmutable immutable = mutable;

        /*
         * Ahhhh Prints out 3 just like I always wanted
         * and I can rely on this super immutable class 
         * never changing. So its thread safe and perfect
         */
        System.out.println(immutable.add());

        /* Some sneak programmer changes a mutable field on the subclass */
        mutable.field3=4;

        /*
         * Ahhh let me just print my immutable 
         * reference again because I can trust it 
         * so much.
         * 
         */
        System.out.println(immutable.add());

        /* Why is this buggy piece of crap printing 7 and not 3
           It couldn't have changed its IMMUTABLE!!!! 
         */
    }

}

/* This class adheres to all the principles of 
*  good immutable classes. All the members are private final
*  the add() method doesn't modify any state. This class is 
*  just a thing of beauty. Its only missing one thing
*  I didn't declare the class final. Let the chaos ensue
*/ 
public class ImSoImmutable{
    private final int field1;
    private final int field2;

    public ImSoImmutable(int field1, int field2){
        this.field1 = field1;
        this.field2 = field2;
    }

    public int add(){
        return field1+field2;
    }
}

/*
This class is the problem. The problem is the 
overridden method add(). Because it uses a mutable 
member it means that I can't  guarantee that all instances
of ImSoImmutable are actually immutable.
*/ 
public class OhNoMutable extends ImSoImmutable{   

    public int field3 = 0;

    public OhNoMutable(int field1, int field2){
        super(field1, field2);          
    }

    public int add(){
       return super.add()+field3;  
    }

}

Trong thực tế, rất thường gặp vấn đề trên trong môi trường Dependency Injection. Bạn không khởi tạo mọi thứ một cách rõ ràng và tham chiếu siêu lớp mà bạn được cung cấp thực sự có thể là một lớp con.

Điều khó khăn là để đảm bảo chắc chắn về tính bất biến, bạn phải đánh dấu lớp là final. Điều này được đề cập sâu trong Java hiệu quả của Joshua Bloch và được tham chiếu rõ ràng trong đặc tả cho mô hình bộ nhớ Java .


những gì về tất cả các thành viên tĩnh?
Neel Salpe

1
lớp học không cần phải là cuối cùng cho điều đó.
Angel O'Sphere

12
@Nilesh: tính bất biến là thuộc tính của các cá thể . Các thành viên tĩnh thường không liên quan đến bất kỳ trường hợp đơn lẻ nào, vì vậy chúng không đi vào hình ảnh ở đây.
Joachim Sauer

4
Mục 15 của Joshua Bloch về tính bất biến - Không có phương thức nào sửa đổi trạng thái, Tất cả các trường là cuối cùng, Tất cả các trường là riêng tư, Đảm bảo rằng lớp không thể mở rộng, Đảm bảo quyền truy cập độc quyền vào bất kỳ thành phần có thể thay đổi nào.
nsfyn55

2
@Jaochim - Chúng hoàn toàn là một phần của phương trình - Lấy ví dụ ở trên nếu tôi thêm một thành viên tĩnh có thể thay đổi và sử dụng nó trong chức năng thêm của ImSoImmutable, bạn gặp vấn đề tương tự. Nếu một lớp là bất biến thì tất cả các khía cạnh phải là bất biến.
nsfyn55

14

Chỉ cần không thêm các phương thức đột biến công khai (setter) vào lớp.


những gì về tất cả các thành viên tĩnh? tham chiếu hoặc trạng thái của đối tượng có thay đổi đối với loại đối tượng đó không?
Neel Salpe

7
Không thành vấn đề. Nếu bạn không thể thay đổi chúng bên ngoài bằng một số phương pháp, thì nó là bất biến.
BalusC

điều đó không thể được trả lời vì chúng tôi không biết các memebers tĩnh làm gì ... ofc họ có thể sửa đổi các trường riêng tư. Nếu họ làm điều đó thì lớp không thể thay đổi được.
Angel O'Sphere

Và phương thức khởi tạo mặc định của lớp nên là privatehoặc lớp phải là final. Chỉ để tránh thừa kế. Bởi vì kế thừa vi phạm tính đóng gói.
Talha Ahmed Khan

Điều gì về việc truyền một đối tượng có thể thay đổi, ví dụ: Danh sách vào đối tượng không thể thay đổi sau đó thay đổi nó từ bên ngoài .. điều này có thể và nó nên được xử lý bằng cách sử dụng các bản sao phòng thủ trong quá trình tạo đối tượng
Yassin Hajaj

14

Các lớp không phải là bất biến, các đối tượng là.

Bất biến có nghĩa là: trạng thái hiển thị công khai của tôi không thể thay đổi sau khi khởi tạo.

Các trường không nhất thiết phải được khai báo cuối cùng, mặc dù nó có thể giúp ích rất nhiều để đảm bảo an toàn cho luồng

Nếu lớp của bạn chỉ có các thành viên tĩnh, thì các đối tượng của lớp này là bất biến, vì bạn không thể thay đổi trạng thái của đối tượng đó (có thể bạn cũng không thể tạo nó :))


3
làm cho tất cả các trường tĩnh giới hạn tất cả các nội dung để chia sẻ cùng một trạng thái, điều này không thực sự hữu ích.
aviad

6

Để làm cho một lớp trở nên bất biến trong Java, bạn có thể lưu ý những điểm sau:

1. Không cung cấp các phương thức setter để sửa đổi giá trị của bất kỳ biến cá thể nào của lớp.

2. Khai báo lớp là 'cuối cùng' . Điều này sẽ ngăn không cho bất kỳ lớp nào khác mở rộng nó và do đó không ghi đè bất kỳ phương thức nào từ nó mà có thể sửa đổi các giá trị biến cá thể.

3. Khai báo các biến instance là private và final .

4. Bạn cũng có thể khai báo hàm tạo của lớp là private và thêm một phương thức factory để tạo một thể hiện của lớp khi được yêu cầu.

Những điểm này sẽ giúp !!


3
WRT # 4 Độ nhớt của hàm tạo ảnh hưởng như thế nào đến tính ổn định? Chuỗi là bất biến nhưng có một số hàm tạo công khai.
Ryan

Như những gì @Ryan đã nói, điều tương tự cũng áp dụng cho các biến cá thể: tại sao chúng phải được khai báo private?
MC Emperor

Không chắc tại sao câu trả lời này có bất kỳ ủng hộ nào. Câu trả lời này là không đầy đủ. Nó không nói về các đối tượng có thể thay đổi được là loại quan trọng cần được giải quyết. Vui lòng đọc phần giải thích của @ nsfyn55 để hiểu rõ hơn.
Ketan R

4

Từ trang oracle , cách tạo các đối tượng bất biến trong Java.

  1. Không cung cấp các phương thức "setter" - các phương thức sửa đổi các trường hoặc đối tượng được các trường tham chiếu.
  2. Đặt tất cả các trường cuối cùng và riêng tư.
  3. Không cho phép các lớp con ghi đè các phương thức. Cách đơn giản nhất để làm điều này là khai báo lớp là cuối cùng. Một cách tiếp cận phức tạp hơn là đặt phương thức khởi tạo là riêng tư và tạo các thể hiện trong các phương thức của nhà máy.
  4. Nếu các trường cá thể bao gồm các tham chiếu đến các đối tượng có thể thay đổi, không cho phép các đối tượng đó được thay đổi:
    I. Không cung cấp các phương thức sửa đổi các đối tượng có thể thay đổi.
    II. Không chia sẻ các tham chiếu đến các đối tượng có thể thay đổi. Không bao giờ lưu trữ các tham chiếu đến các đối tượng bên ngoài, có thể thay đổi được chuyển cho hàm tạo; nếu cần, hãy tạo các bản sao và lưu trữ các tham chiếu đến các bản sao. Tương tự, hãy tạo bản sao của các đối tượng có thể thay đổi bên trong của bạn khi cần thiết để tránh trả lại bản gốc trong các phương thức của bạn.

3

Một đối tượng không thay đổi là một đối tượng sẽ không thay đổi trạng thái bên trong của nó sau khi tạo. Chúng rất hữu ích trong các ứng dụng đa luồng vì chúng có thể được chia sẻ giữa các luồng mà không cần đồng bộ hóa.

Để tạo một đối tượng không thay đổi, bạn cần tuân theo một số quy tắc đơn giản:

1. Không thêm bất kỳ phương thức setter nào

Nếu bạn đang xây dựng một đối tượng bất biến thì trạng thái bên trong của nó sẽ không bao giờ thay đổi. Nhiệm vụ của phương thức setter là thay đổi giá trị bên trong của một trường, vì vậy bạn không thể thêm nó.

2. Khai báo tất cả các trường cuối cùng và riêng tư

Trường riêng tư không hiển thị từ bên ngoài lớp nên không thể áp dụng các thay đổi thủ công cho trường đó.

Khai báo một trường cuối cùng sẽ đảm bảo rằng nếu nó tham chiếu đến một giá trị nguyên thủy thì giá trị sẽ không bao giờ thay đổi nếu nó tham chiếu đến một đối tượng thì không thể thay đổi tham chiếu. Điều này không đủ để đảm bảo rằng một đối tượng chỉ có các trường cuối cùng riêng tư là không thể thay đổi.

3. Nếu một trường là một đối tượng có thể thay đổi, hãy tạo các bản sao phòng thủ của nó cho các phương thức getter

Chúng ta đã thấy trước đây rằng việc xác định trường cuối cùng và riêng tư là không đủ vì có thể thay đổi trạng thái bên trong của nó. Để giải quyết vấn đề này, chúng ta cần tạo một bản sao phòng thủ của trường đó và trả lại trường đó mỗi khi nó được yêu cầu.

4. Nếu một đối tượng có thể thay đổi được chuyển cho hàm tạo phải được gán cho một trường, hãy tạo một bản sao bảo vệ của nó

Vấn đề tương tự cũng xảy ra nếu bạn giữ một tham chiếu được truyền cho hàm tạo vì bạn có thể thay đổi nó. Vì vậy, việc giữ một tham chiếu đến một đối tượng được truyền cho hàm tạo có thể tạo ra các đối tượng có thể thay đổi. Để giải quyết vấn đề này, cần tạo một bản sao bảo vệ của tham số nếu chúng là các đối tượng có thể thay đổi.

Lưu ý rằng nếu một trường là một tham chiếu đến một đối tượng không thể thay đổi thì không cần thiết phải tạo các bản sao bảo vệ của nó trong hàm tạo và trong các phương thức getter, nó đủ để xác định trường là cuối cùng và riêng tư.

5. Không cho phép các lớp con ghi đè các phương thức

Nếu một lớp con ghi đè một phương thức, nó có thể trả về giá trị ban đầu của một trường có thể thay đổi thay vì một bản sao bảo vệ của nó.

Để giải quyết vấn đề này, bạn có thể thực hiện một trong những cách sau:

  1. Khai báo lớp không thay đổi là lớp cuối cùng để nó không thể mở rộng
  2. Khai báo tất cả các phương thức của lớp cuối cùng bất biến để chúng không thể bị ghi đè
  3. Tạo một phương thức khởi tạo riêng và một nhà máy để tạo các thể hiện của lớp bất biến vì không thể mở rộng một lớp có các hàm tạo riêng

Nếu bạn tuân theo những quy tắc đơn giản đó, bạn có thể thoải mái chia sẻ các đối tượng bất biến của mình giữa các luồng vì chúng là luồng an toàn!

Dưới đây là một số điểm đáng chú ý:

  • Các đối tượng bất biến thực sự làm cho cuộc sống đơn giản hơn trong nhiều trường hợp. Chúng đặc biệt áp dụng cho các loại giá trị, nơi các đối tượng không có danh tính để chúng có thể dễ dàng thay thế và chúng có thể làm cho cách lập trình đồng thời an toàn hơn và sạch sẽ hơn (hầu hết các lỗi đồng thời nổi tiếng khó tìm cuối cùng là do trạng thái có thể thay đổi được chia sẻ giữa chủ đề). Tuy nhiên, đối với các đối tượng lớn và / hoặc phức tạp, việc tạo một bản sao mới của đối tượng cho mọi thay đổi có thể rất tốn kém và / hoặc tẻ nhạt . Và đối với các đối tượng có danh tính riêng biệt, việc thay đổi đối tượng hiện có đơn giản và trực quan hơn nhiều so với việc tạo một bản sao mới, sửa đổi của nó.
  • Có một số điều bạn không thể làm với các đối tượng bất biến, chẳng hạn như có các mối quan hệ hai chiều . Khi bạn đặt giá trị liên kết trên một đối tượng, danh tính của nó sẽ thay đổi. Vì vậy, bạn đặt giá trị mới trên đối tượng khác và nó cũng thay đổi theo. Vấn đề là tham chiếu của đối tượng đầu tiên không còn hợp lệ, bởi vì một thể hiện mới đã được tạo để đại diện cho đối tượng với tham chiếu. Tiếp tục điều này sẽ chỉ dẫn đến hồi quy vô hạn.
  • Để triển khai cây tìm kiếm nhị phân , bạn phải trả lại một cây mới mỗi lần: Cây mới của bạn sẽ phải tạo một bản sao của mỗi nút đã được sửa đổi (các nhánh chưa được sửa đổi được chia sẻ). Đối với chức năng chèn của bạn, điều này không quá tệ, nhưng đối với tôi, mọi thứ trở nên kém hiệu quả một cách nhanh chóng khi tôi bắt đầu thực hiện xóa và cân bằng lại.
  • Hibernate và JPA về cơ bản ra lệnh rằng hệ thống của bạn sử dụng các đối tượng có thể thay đổi, bởi vì toàn bộ tiền đề của chúng là chúng phát hiện và lưu các thay đổi đối với các đối tượng dữ liệu của bạn.
  • Tùy thuộc vào ngôn ngữ, trình biên dịch có thể thực hiện một loạt các tối ưu hóa khi xử lý dữ liệu bất biến vì nó biết dữ liệu sẽ không bao giờ thay đổi. Tất cả các loại nội dung đều được bỏ qua, mang lại cho bạn những lợi ích to lớn về hiệu suất.
  • Nếu bạn nhìn vào các ngôn ngữ JVM đã biết khác ( Scala, Clojure ), hiếm khi thấy các đối tượng có thể thay đổi được trong mã và đó là lý do tại sao mọi người bắt đầu sử dụng chúng trong các tình huống mà luồng đơn là không đủ.

Không có đúng hay sai, nó chỉ phụ thuộc vào những gì bạn thích. Nó chỉ phụ thuộc vào sở thích của bạn và những gì bạn muốn đạt được (và có thể dễ dàng sử dụng cả hai cách tiếp cận mà không làm xa lánh những người hâm mộ khó tính của bên này hay bên khác là một chén thánh mà một số ngôn ngữ đang tìm kiếm).


2
  • Không cung cấp các phương thức "setter" - các phương thức sửa đổi các trường hoặc đối tượng được các trường tham chiếu.
  • Đặt tất cả các trường cuối cùng và riêng tư.
  • Không cho phép các lớp con ghi đè các phương thức. Cách đơn giản nhất để làm điều này là khai báo lớp là cuối cùng. Một cách tiếp cận phức tạp hơn là đặt phương thức khởi tạo là riêng tư và tạo các thể hiện trong các phương thức của nhà máy.
  • Nếu các trường cá thể bao gồm tham chiếu đến các đối tượng có thể thay đổi, không cho phép thay đổi các đối tượng đó:
    • Không cung cấp các phương thức sửa đổi các đối tượng có thể thay đổi.
    • Không chia sẻ các tham chiếu đến các đối tượng có thể thay đổi. Không bao giờ lưu trữ các tham chiếu đến các đối tượng bên ngoài, có thể thay đổi được chuyển cho hàm tạo; nếu cần, hãy tạo các bản sao và lưu trữ các tham chiếu đến các bản sao. Tương tự, hãy tạo bản sao của các đối tượng có thể thay đổi bên trong của bạn khi cần thiết để tránh trả lại bản gốc trong các phương thức của bạn.

2

Trước hết, bạn biết lý do tại sao bạn cần tạo đối tượng không thay đổi, và ưu điểm của đối tượng không thay đổi là gì.

Ưu điểm của đối tượng Immutable

Đồng thời và đa luồng Nó tự động An toàn theo luồng nên vấn đề đồng bộ hóa .... vv

Không cần sao chép hàm tạo Không cần thực hiện sao chép. Không thể ghi đè lớp Đặt trường làm trường riêng tư và cuối cùng Buộc người gọi xây dựng một đối tượng hoàn toàn trong một bước duy nhất, thay vì sử dụng một phương thức khởi tạo không đối số

Các đối tượng bất biến chỉ đơn giản là các đối tượng có trạng thái có nghĩa là dữ liệu của đối tượng không thể thay đổi sau khi đối tượng không thay đổi được xây dựng.

xin vui lòng xem mã dưới đây.

public final class ImmutableReminder{
    private final Date remindingDate;

    public ImmutableReminder (Date remindingDate) {
        if(remindingDate.getTime() < System.currentTimeMillis()){
            throw new IllegalArgumentException("Can not set reminder" +
                    " for past time: " + remindingDate);
        }
        this.remindingDate = new Date(remindingDate.getTime());
    }

    public Date getRemindingDate() {
        return (Date) remindingDate.clone();
    }
}

2

Giảm thiểu khả năng thay đổi

Một lớp bất biến chỉ đơn giản là một lớp mà các thể hiện của nó không thể sửa đổi được. Tất cả thông tin có trong mỗi cá thể được cung cấp khi nó được tạo và được cố định trong suốt thời gian tồn tại của đối tượng.

Các lớp bất biến của JDK: Chuỗi, các lớp nguyên thủy đóng hộp (các lớp trình bao bọc), BigInteger và BigDecimal, v.v.

Làm thế nào để biến một lớp trở thành bất biến?

  1. Không cung cấp bất kỳ phương thức nào sửa đổi trạng thái của đối tượng (được gọi là trình đột biến).
  2. Đảm bảo rằng lớp học không thể được mở rộng.
  3. Làm cho tất cả các trường cuối cùng.
  4. Đặt tất cả các trường ở chế độ riêng tư. Điều này ngăn không cho khách hàng truy cập vào các đối tượng có thể thay đổi được các trường tham chiếu và sửa đổi các đối tượng này trực tiếp.
  5. Tạo bản sao phòng thủ. Đảm bảo quyền truy cập độc quyền vào bất kỳ thành phần có thể thay đổi nào.

    public List getList () {return Collections.unmodifiableList (danh sách); <=== bản sao phòng thủ của trường có thể thay đổi trước khi trả lại trường đó cho người gọi}

Nếu lớp của bạn có bất kỳ trường nào tham chiếu đến các đối tượng có thể thay đổi, hãy đảm bảo rằng các máy khách của lớp không thể lấy tham chiếu đến các đối tượng này. Không bao giờ khởi tạo một trường như vậy thành tham chiếu đối tượng do máy khách cung cấp hoặc trả về tham chiếu đối tượng từ trình truy cập.

import java.util.Date;
public final class ImmutableClass {

       public ImmutableClass(int id, String name, Date doj) {
              this.id = id;
              this.name = name;
              this.doj = doj;
       }

       private final int id;
       private final String name;
       private final Date doj;

       public int getId() {
              return id;
       }
       public String getName() {
              return name;
       }

     /**
      * Date class is mutable so we need a little care here.
      * We should not return the reference of original instance variable.
      * Instead a new Date object, with content copied to it, should be returned.
      * */
       public Date getDoj() {
              return new Date(doj.getTime()); // For mutable fields
       }
}
import java.util.Date;
public class TestImmutable {
       public static void main(String[] args) {
              String name = "raj";
              int id = 1;
              Date doj = new Date();

              ImmutableClass class1 = new ImmutableClass(id, name, doj);
              ImmutableClass class2 = new ImmutableClass(id, name, doj);
      // every time will get a new reference for same object. Modification in              reference will not affect the immutability because it is temporary reference.
              Date date = class1.getDoj();
              date.setTime(date.getTime()+122435);
              System.out.println(class1.getDoj()==class2.getDoj());
       }
}

Để biết thêm thông tin, hãy xem blog của tôi:
http://javaexplorer03.blogspot.in/2015/07/minimize-mutability.html


@Pang có cách nào khác để khởi tạo các trường ngoài hàm tạo không. Tôi có hơn 20 lĩnh vực trong lớp của mình. Rất khó để khởi tạo tất cả các trường bằng cách sử dụng hàm tạo, một số trường thậm chí còn là tùy chọn.
Nikhil Mishra

1
@NikhilMishra, bạn có thể sử dụng mẫu thiết kế Builder để khởi tạo các biến trong quá trình xây dựng đối tượng. Bạn có thể giữ các biến bắt buộc được đặt trong hàm tạo và các biến tùy chọn còn lại được đặt bằng các phương thức setter. Nhưng nói đúng ra theo cách này, bạn sẽ không tạo ra một lớp True Immutable.
sunny_dev

1

một đối tượng được gọi là bất biến nếu trạng thái của nó không thể thay đổi sau khi được tạo. Một trong những cách đơn giản nhất để tạo lớp bất biến trong Java là đặt tất cả các trường của nó là cuối cùng. Nếu bạn cần viết lớp không thay đổi bao gồm các lớp có thể thay đổi như "java.util.Date". Để duy trì tính bất biến trong những trường hợp như vậy, họ nên trả lại bản sao của đối tượng gốc,


Nó không phải là việc trả lại một bản sao được khuyên, nó cần thiết. Nhưng nó cũng cần thiết là một bản sao phòng thủ được tạo trong hàm tạo. Nếu không, đối tượng có thể được thay đổi ở hậu trường.
Ryan

1

Đối tượng bất biến là những đối tượng có trạng thái không thể thay đổi khi chúng được tạo ra, ví dụ lớp String là một lớp bất biến. Không thể sửa đổi các đối tượng bất biến vì vậy chúng cũng an toàn cho luồng khi thực thi đồng thời.

Tính năng của các lớp bất biến:

  • đơn giản để xây dựng
  • tự động chủ đề an toàn
  • ứng cử viên tốt cho các phím Bản đồ và Đặt làm trạng thái bên trong của chúng sẽ không thay đổi trong khi xử lý
  • không cần triển khai bản sao vì chúng luôn đại diện cho cùng một trạng thái

Các phím để viết lớp bất biến:

  • đảm bảo lớp không thể bị ghi đè
  • đặt tất cả biến thành viên ở chế độ riêng tư & cuối cùng
  • không đưa ra phương pháp setter của họ
  • đối tượng tham chiếu không nên bị rò rỉ trong giai đoạn xây dựng

1

Một số bước sau đây phải được xem xét, khi bạn muốn bất kỳ lớp nào là một lớp bất biến.

  1. Lớp phải được đánh dấu là cuối cùng
  2. Tất cả các trường phải là trường riêng tư và cuối cùng
  3. Thay thế bộ thiết lập bằng hàm tạo (để gán giá trị cho một biến).

Hãy xem qua những gì chúng tôi đã nhập ở trên:

//ImmutableClass
package younus.attari;

public final class ImmutableExample {

    private final String name;
    private final String address;

    public ImmutableExample(String name,String address){
        this.name=name;
        this.address=address;
    }


    public String getName() {
        return name;
    }

    public String getAddress() {
        return address;
    }

}

//MainClass from where an ImmutableClass will be called
package younus.attari;

public class MainClass {

    public static void main(String[] args) {
        ImmutableExample example=new ImmutableExample("Muhammed", "Hyderabad");
        System.out.println(example.getName());

    }
}

0

Các thuộc tính thường bị bỏ qua nhưng quan trọng trên các đối tượng bất biến

Thêm giao cho câu trả lời cung cấp bởi @ nsfyn55, các khía cạnh sau cũng cần phải được xem xét tính bất biến đối tượng, đó là các số nguyên tố quan trọng

Hãy xem xét các lớp sau:

public final class ImmutableClass {

  private final MutableClass mc;

  public ImmutableClass(MutableClass mc) {
    this.mc = mc;
  }

  public MutableClass getMutClass() {
    return this.mc;
  }
}

public class MutableClass {

  private String name;

  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }
}


public class MutabilityCheck {

public static void main(String[] args) {

  MutableClass mc = new MutableClass();

  mc.setName("Foo");

  ImmutableClass iMC = new ImmutableClass(mc);

  System.out.println(iMC.getMutClass().getName());

  mc.setName("Bar");

  System.out.println(iMC.getMutClass().getName());

  }

 }

Sau đây sẽ là đầu ra từ MutabilityCheck:

 Foo
 Bar

Điều quan trọng cần lưu ý là,

  1. Xây dựng các đối tượng có thể thay đổi trên một đối tượng không thể thay đổi (thông qua hàm tạo), bằng cách 'sao chép' hoặc 'nhân bản' vào các biến thể hiện của bất biến được mô tả bằng các thay đổi sau:

    public final class ImmutableClass {
    
       private final MutableClass mc;
    
       public ImmutableClass(MutableClass mc) {
         this.mc = new MutableClass(mc);
       }
    
       public MutableClass getMutClass() {
         return this.mc;
       }
    
     }
    
     public class MutableClass {
    
      private String name;
    
      public MutableClass() {
    
      }
      //copy constructor
      public MutableClass(MutableClass mc) {
        this.name = mc.getName();
      }
    
      public String getName() {
        return this.name;
      }
    
      public void setName(String name) {
       this.name = name;
      } 
     }
    

vẫn không đảm bảo tính bất biến hoàn toàn vì điều sau vẫn còn hiệu lực từ Kiểm tra tính ổn định của lớp:

  iMC.getMutClass().setName("Blaa");
  1. Tuy nhiên, chạy MutabilityCheck với các thay đổi được thực hiện trong 1. sẽ dẫn đến kết quả là:

    Foo
    Foo
    
  2. Để đạt được tính bất biến hoàn toàn trên một đối tượng, tất cả các đối tượng phụ thuộc của nó cũng phải là bất biến


0

Từ JDK 14+ có JEP 359 , chúng ta có thể sử dụng " records". Đây là cách đơn giản và hối hả để tạo lớp Immutable.

Lớp bản ghi là một sóng mang trong suốt, không thay đổi nông cho một tập hợp các trường cố định được gọi là bản ghi componentscung cấp statemô tả cho bản ghi. Mỗi thứ componentlàm phát sinh một finaltrường chứa giá trị đã cung cấp và một accessorphương thức để truy xuất giá trị. Tên trường và tên trình truy cập khớp với tên của thành phần.

Hãy xem xét ví dụ về việc tạo một hình chữ nhật bất biến

record Rectangle(double length, double width) {}

Không cần khai báo bất kỳ hàm tạo nào, không cần triển khai equals& hashCodephương thức. Chỉ cần bất kỳ Hồ sơ nào cần có tên và mô tả trạng thái.

var rectangle = new Rectangle(7.1, 8.9);
System.out.print(rectangle.length()); // prints 7.1

Nếu bạn muốn xác thực giá trị trong quá trình tạo đối tượng, chúng ta phải khai báo hàm tạo một cách rõ ràng.

public Rectangle {

    if (length <= 0.0) {
      throw new IllegalArgumentException();
    }
  }

Phần thân của bản ghi có thể khai báo các phương thức tĩnh, trường tĩnh, bộ khởi tạo tĩnh, hàm tạo, phương thức thể hiện và các kiểu lồng nhau.

Phương pháp phiên bản

record Rectangle(double length, double width) {

  public double area() {
    return this.length * this.width;
  }
}

trường tĩnh, phương thức

Vì trạng thái phải là một phần của các thành phần, chúng tôi không thể thêm trường cá thể vào bản ghi. Tuy nhiên, chúng ta có thể thêm các trường và phương thức tĩnh:

record Rectangle(double length, double width) {

  static double aStaticField;

  static void aStaticMethod() {
    System.out.println("Hello Static");
  }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.