Java中建立不可變的類

2016-04-22    分類:JAVA開發、程式設計開發、首頁精華0人評論發表於2016-04-22

本文由碼農網 – 栗子蜀黍原創翻譯,轉載請看清文末的轉載要求,歡迎參與我們的付費投稿計劃

簡介:本文主要介紹java中不可變類的相關知識,文章中大部分內容來自部落格,部落格地址見以下連結

Creating Immutable Classes in Java

小編不去糾結原文中,為什麼標題是immutable class,正文卻上來便問what is immutable object。相信,學java的人,都應該知道class和object確切表示什麼意思。

class:java中class確切的表示為一個類

object:java中object確切的表示為一個物件,也稱為類的例項

其實,如果一個類被設計成不可變的類,那麼這個類的例項化物件也是不可變的。

不可變類:當你獲得這個類的一個例項引用時,你不可以改變這個例項的內容。

那麼,什麼是不可變物件?

一旦一個類的例項化物件被建立並初始化,那麼它就不可以被改變。我們可以呼叫訪問器方法(getter),複製物件,或者傳遞物件,但是不允許任何方法改變這個物件的狀態。包裝類(e.g.Integer或Float)和String類是不可變類的代表。

訪問器方法(accessor method):對成員變數做出訪問的方法,e.g.getter()方法。

修改器方法(mutator method):對成員變數做出修改的方法,e.g.setter()方法。

定義一個不可變類

如果我們要自己建立一個不可變類,需要遵守下面的規則:

將成員變數(field:在一些書中也翻譯為域)宣告成final並在構造器中初始化。

對於基本型別的成員變數,用final修飾,一旦它被初始化,就不能被改變了。而對於引用型別的成員變數,不能夠改變它的引用。

成員變數如果被宣告稱final,那麼構建物件時,必須要初始化這樣的域

引用型別是可變的,我們需要採取一些措施來保證它的不可變性。

為什麼?如果我們只是宣告瞭一個final的可變引用型別,那麼這個引用可以去引用外部的類,或者被其他外部類引用。在這種情況下,我們要做到:

1.這些方法不會改變這些可變物件中的內容

2.不要將這些引用分享到外部供其他類使用,例如,如果對成員變數的引用是可以被其他類改變的,那麼這些外部類就可以改變這個類中的內容。

3.如果必須要返回一個引用,那麼就返回一個物件的深度拷貝,這樣儘管返回的物件內容改變了,但也儲存著原始的內容。

只提供訪問器方法(i.e. getter方法)不提供修改器方法(i.e.setter方法)

如果一定要改變這個物件的內容,那就建立一個新的不可變物件內容做相應的修改,返回修改後的物件的引用

宣告類是final的。如果一個類可以被繼承,那麼它子類就可以過載它的方法,並且修改成員變數

Java API中不可變類的例子

讓我們來回顧一下String類,用它來理解上述的幾個方面在String類實現中的體現:

所有在Stirng類中成員變數都被宣告成private,這些成員變數都在構造器中在構建物件時被初始化。

trim concat substring 都可以改變String的物件,為了保證String的不可變性,這些方法都返回的是一個改變相應內容後新的物件。

string類被宣告稱final,所以任何類都不能繼承,過載它的方法。

自己實現一個不可變類

接下來我們自己實現一個不可變類ImmutableCircle。

//ImmutableCircle.java
// Point is a mutable class
class Point {
    private int xPos, yPos;
    public Point(int x, int y) {
       xPos = x;
       yPos = y;
    }
    public String toString() {
        return "x = " + xPos + ", y = " + yPos;
    }
    int getX() { return xPos; }
    int getY() { return yPos; }
}
// ImmutableCircle is an immutable class – the state of its objects
// cannot be modified once the object is created
public final class ImmutableCircle {
private final Point center;
private final int radius;
public ImmutableCircle(int x, int y, int r) {
center = new Point(x, y);
radius = r;
}
public String toString() {
return "center: " + center + " and radius = " + radius;
}
public int getRadius() {
return radius;
}
public Point getCenter() {
// return a copy of the object to avoid
// the value of center changed from code outside the class
return new Point(center.getX(), center.getY());
}
public static void main(String []s) {
System.out.println(new ImmutableCircle(10, 10, 20));
}
// other members are elided ...
}

上面的程式執行之後,列印:

center: x = 10, y = 10 and radius = 20

上面的程式體現了不可變類的以下幾點:

  • 這個類被宣告成final,不可以被繼承,也不可以過載它的方法
  • 這個類的成員變數都是final並且是私有的
  • 因為成員變數center是一個引用型別,是可變的,所以在他的getter方法中,返回的是對point物件的拷貝

設計一個不可變的類最關鍵的一點:

要注意引用型別的成員變數,如果成員變數的型別是可變的引用型別,就必須要採取必要的措施來保護這個成員變數不會被修改

不可變類不足的地方

不可變物件同樣也有不足的地方。為了保證不可變性,不可變類中的方法會建立出一定量的物件的拷貝。例如,在上面的程式碼中,每次呼叫getcenter方法都會新建並返回一個point物件的拷貝。而假如我們只需要呼叫一次,返回一個point物件,就沒必要費盡心神的去設計一個不可變類,僅僅只需要一個可變的immutablecircle類就可以了。

String類在很多應用場景中都會用到,如果我們呼叫String類中trim,concat,或者是在迴圈中呼叫substring方法,都會建立一個新的臨時String物件。同時,java也提供了Stringbuffer和Stringbuilder的可變類。他們同String一樣,但是卻可以改變這個物件的內容。所以,我們可以根據不同的場景使用String類或者Stringbuffer/Stringbuilder類。

總結,文章的最後還是那句話,要根據自己的實際需要,去設計程式碼,而不要過度設計。

譯文連結:http://www.codeceo.com/article/java-create-immutable-classes.html
英文原文:Creating Immutable Classes in Java
翻譯作者:碼農網 – 栗子蜀黍
轉載必須在正文中標註並保留原文連結、譯文連結和譯者等資訊。]

相關文章