2012-12-06 18 views
4

我想複製一個對象,然後將被修改,而無需更改原始對象。複製一個對象在Java中,而不會影響原始通過複製構造函數

我發現this solution它似乎最好的方法是複製構造函數 - 從我的理解,這會給我一個深層複製(與原始的完全獨立的對象)。

所以我試過了。但是,我注意到,當下面的代碼執行時,它會影響所有以前從中複製的對象。當我撥打surveyCopy.take()時,會改變Survey中的值,它也會更改selectedSurvey中的值。

public class MainDriver { 
... 
//Code that is supposed to create the copy 
case "11": selectedSurvey = retrieveBlankSurvey(currentSurveys); 
      Survey surveyCopy = new Survey(selectedSurvey); 
      surveyCopy.take(consoleIO); 
      currentSurveys.add(surveyCopy); 
      break; 
} 

,這裏是我的拷貝構造函數代碼:

public class Survey implements Serializable 
{ 
    ArrayList<Question> questionList; 
    int numQuestions; 
    String taker; 
    String surveyName; 
    boolean isTaken; 

    //Copy constructor 
    public Survey(Survey incoming) 
    { 
     this.taker = incoming.getTaker(); 
     this.numQuestions = incoming.getNumQuestions(); 
     this.questionList = incoming.getQuestionList(); 
     this.surveyName = incoming.getSurveyName(); 
     this.isTaken = incoming.isTaken(); 
    } 
} 

那麼究竟是什麼問題?複製構造函數是不是以這種方式工作?我編碼錯了嗎?

回答

8

this.questionList = incoming.getQuestionList(); 

最有可能複製參考到原始列表(我說可能,因爲它可能是getQuestionList()給你一個防守副本)。您可能需要製作該列表的新副本。也許包含的對象是Question。也許他們提到的任何東西。

這是深拷貝的問題。爲了可靠地執行此操作,必須複製所有可變對象。請注意,如果一個對象是不可變的(例如字符串),則不能更改它們,因此您可以參考原件確信它們不會被更改。原語也是如此。在您的代碼庫中鼓勵不變性的一個很好的理由。

如果你不能建立一個不可變的類,請寫下你的類,使它成爲防禦性的副本。即當客戶要求收集時,應該複製並返回。否則,你所謂的善意的客戶可能會改變你的內部狀態(無意或無意)。

+0

它*絕對*複製參考 – mishadoff

+0

我明白你的觀點。我試圖說明該方法可以返回一個防禦副本(即它會創建一個副本,然後返回它)。無論如何,參考副本將在某處發生 –

+0

@mishadoff - 不一定。我們沒有看到getter代碼,並且一些getter實際上創建了一個防禦副本(對於深度克隆來說,這可以很好地工作)。 「我認爲,」很可能複製了參考文獻「是一個正確的解釋。 – mikera

13

這是問題,在您的拷貝構造函數:

this.questionList = incoming.getQuestionList(); 

這只是複製參考到列表中。兩個對象仍然會引用同一個對象。

您可以使用:

this.questionList = new ArrayList<Question>(incoming.getQuestionList()); 

創建原始列表的副本 - 但這仍然不夠好,如果Question本身是可變的。在這種情況下,您必須創建每個對象的副本以實現完全隔離。

您的其他領域沒問題,因爲它們是原始圖片或對String的引用(它是不可變的,允許您安全地共享引用)。

+0

感謝您的回覆。這聽起來像我應該讓'問題'不變,因爲我不確定它是否是。我怎樣才能做到這一點?編輯:也許這不是最好的主意,因爲在問題裏有更多的對象是不可變的。我很難過。 – iaacp

+0

@iaacp:是的,如果你可以讓'Question'不變,這將有所幫助。如果它的所有字段都是不可變類型的,那麼它應該使它更容易*而不是更難。 –

5

創建深度副本時的問題是,除非您使用特定的深層副本構造函數,否則不是基本類型的所有內容都會被引用複製。

在特定情況下,你有boolintString變量沒有問題,因爲你通過他們周圍的值(實際上String按引用傳遞,但它是不可變的,所以沒有問題),但你傳遞一個ArrayList<Question> questionList。當你做

this.object = incoming.object 

你只是複製一個引用。所以這兩個變量都指向內存中的同一個對象,所以你並沒有深究它。您必須創建具有相同內部值的對象的另一個實例,然後您將確定,例如this.object = new YourObject(incoming.object)

請注意,通常意味着在作文樹中複雜程度越高,您將不得不深入變量,直到您複製所有變量。