歡迎光臨
每天分享高質量文章

回到基礎:封裝集合

(點選上方公眾號,可快速關註)


ImportNew – 文學敏

以前學面向物件時,瞭解到它有三種特性:

  • 封裝

  • 繼承

  • 多型

Java中封裝的實現,是透過為私有成員提供訪問器方法,即通常所知的getter和setter方法。這樣封裝是否合適仍屬爭議,也超出了本文的討論範圍。但是,當成員變數為集合型別(java.util.Collection,java.util.Map以及它們的子類)時,這樣實現封裝是完全錯誤的。

我經常能見到的程式碼像下麵這樣:

public class MyBean {

    private Collection collection;

 

    public Collection getCollection() {

        return collection;

    }

 

    public void setCollection(Collection collection) {

        this.collection = collection;

    }

}

就我所見,這樣的程式碼很普遍,這是由於Hibernate等ORM框架使得這種設計變得流行。很多時候,當我提出我的觀點,得到的建議就是使用一種不可變的設計:

public class MyBean {

    private Collection collection;

 

    public MyBean(Collection collection) {

        this.collection = collection;

    }

 

    public Collection getCollection() {

        return collection;

    }

}

不合適的封裝

然而,在使用集合型別的情形下,由於Java中集合型別自身是可變的,這其實並沒有任何改變。很明顯,無論是透過建構式傳入一個集合實體的取用,還是傳回它的取用,這完全沒有進行封裝。只有當集合實體的取用沒有(在外部)保留,也不會傳回(到外部),真正的封裝才有可能實現。

List list = new ArrayList();

MyBean mybean = new MyBean(list);

list.add(new Object()); // 我們在mybean外部改變了封裝的集合

不能使用具體的子類

另外,MyBean類可能需要封裝一種更具體的集合類,比如List或者Set。從下麵的程式碼片段可以看出,傳入一個Set實體是不可能的。

public class MyBean {

    private List collection;

 

    public List getCollection() {

        return collection;

    }

 

    public void setCollection(List collection) {

        this.collection = collection;

    }

}

不能選擇具體的實現

由上一點很自然地想到,使用(外部)提供的取用的話,我們也無法使用(可能為了更高效)自己定義的類,比如Apache Commons的FastArrayList。

實現建議

下麵的程式碼做到了真正封裝的出發點。

public class MyBean {

    private List collection = new ArrayList();

 

    public MyBean(Collection collection) {

        this.collection.addAll(collection);

    }

 

    public Collection getCollection() {

        return Collections.unmodifiableList(collection);

    }

}

這種方式解決了前面提到的幾個問題:

  • 集合實體的取用沒有從建構式中傳入,這樣就不可能在實體外部改變實體。

  • 由於完全隔離,可以自由地選擇集合的實現,為修改留下餘地。

  • 不能透過getter訪問器方法獲得被封裝的集合實體的取用。

註意:為了可讀性,前面的程式碼片段沒有使用泛型。請在實際使用中加上。

看完本文有收穫?請轉發分享給更多人

關註「ImportNew」,提升Java技能

贊(0)

分享創造快樂